breaking out of an underscore each - javascript

I am trying to find a model within a collection with an attribute equal to html select option value.
<div id="hospital-details">
<select name="hospitalnames">
<option><%- model.get('name') %></option>
</select>
</div>
whenever hospital name is changed, jquery change callback is triggered to find locationModel with selected option value as attribute value as shown below,
$('select[name="hospitalnames"]').change(function() {
var name = $(this).val();
locationListCollection.each(function(locationModel) {
if ($.trim(locationModel.get('name')) == $.trim(name)) {
that.locationModel = locationModel;
return false; // control is returned to underscore.min.js
}
});
});
console.log(that.locationModel); // this is not being displayed at all
After the locationModel with an attribute is found, I am unable to come out the loop. Any help ? At this moment I have looked into
this but without success.

You're using the wrong method if you're searching for the first match. Collections have lots of Underscore methods mixed in, in particular they have find mixed in:
find _.find(list, iterator, [context])
Looks through each value in the list, returning the first one that passes a truth test (iterator), or undefined if no value passes the test.
Something like this:
var name = $.trim($(this).val());
that.locationModel = locationListCollection.find(function(locationModel) {
return $.trim(locationModel.get('name')) == name;
});
and if the names in your model are pre-trimmed and nice and clean, then you could use findWhere:
findWhere collection.findWhere(attributes)
Just like where, but directly returns only the first model in the collection that matches the passed attributes.
like this:
var name = $.trim($(this).val());
that.locationModel = locationListCollection.findWhere({ name: name });
BTW, this:
console.log(locationModel);
won't give you anything because locationModel and that.locationModel are different things.

You can always go oldschool.
$('select[name="hospitalnames"]').change(function() {
var name = $(this).val();
for (var i = 0; i < locationListCollection.length; ++i) {
var locationModel = locationListCollection.models[i];
if ($.trim(locationModel.get('name')) == $.trim(name)) {
that.locationModel = locationModel;
break;
}
}
});

Try this,
var name = $(this).val();
var flag=true;
locationListCollection.each(function(locationModel) {
if (flag && $.trim(locationModel.get('name')) == $.trim(name)) {
that.locationModel = locationModel;
flag=false;
//return false;// to break the $.each loop
}
});

The short is no.
If you take a look at underscore's source you'll see that they use a breaker object to quickly stop a .each() but that is only available internally.
I would not recommend this but you could always modify the source to expose this breaker object (see baseline setup in the annotated source
http://underscorejs.org/docs/underscore.html). Then you would just return this object instead of returning false. But you would probably need to remove the native forEach call to keep the behaviour consistent. So it's not worth it!
_.each(function(arr) {
if(condition) {
return _.breaker; // Assuming you changed the source.
}
});
Since you are searching for a single item instead of .each() use:
var locationModel = _.find(arr, function(item) {
return $.trim(locationModel.get('name')) == $.trim(name);
));

Related

why doesn't this return an l-value?

Maybe ReSharper or visual studio is wrong, but I don't think that this returns an r-value. I also don't think it actually sets the property in the $parent controller:
function getParentItem(path) {
var obj = $scope.$parent;
var param = null;
var items = path.split(".");
for (var i = 0; i < items.length; i++) {
var item = items[i];
var split = item.split("(");
if (split.length === 2) {
param = split[1].replace(/[\)\']/g, "");
}
obj = obj[split[0]];
}
if (param == null) {
var thisObj = obj;
return thisObj;
} else {
return { obj: obj, param: param };
}
}
If I do this:
getParentItem($scope.someProperty) = "yadda"
I get error marked by probably ReSharper and I think it doesn't actually set the new value
As Amy/Volkan said your code is not valid but I think I get what you want to do. There are lots of ifs but here it goes:
if your $scope.someProperty is string property that you want to reassign on result of the function getParentItem, and your function returns object that can have that param ($scope.someProperty), first you need to figure out which path you pass in but it looks like it's some string separated by dots.
// so then assign result of the function to some variable
// you need to pass somePath to function
let parentItem = getParentItem(somePath);
// then change that property
parentItem[$scope.someProperty] = "yadda";
or another possibility what you might need would be:
parentItem.param[$scope.someProperty] = "yadda";
then do whatever you want with parentItem like put it on $scope or whatever.
If you want better help please do some jsfiddle or something.
The problem is (and I slap my head on how stupid I was) that the leaf branches of this $scope object aren't objects themselves, and in some cases in our code they don't even exist yet. You get so used to $scope being an object you fail to realize that the final elements can't possibly be objects at least in Javascript.
So the solution was to pass the value that I wanted to set as a parameter:
function getParentItem(path, optionalValue)
On the final loop of the parent search, if optionalValue is passed, I can then set the value onto the object:
obj[--last parameter name--] = optionalValue;

How to remove elements from a selector

I'm struggling to get a piece of code to work but I'm not a jquery guy so please bear with me.
I have an outer DIV ($scope). It contains all kinds of inputs.
I find all the entries for each input type and filter them to get the ones with values. These are stored in $entries.
$inputs contains all the inputs regardless of type or status.
What I'm trying to do is remove $entries from $inputs to leave the difference.
It doesn't work, and at the moment I'm not getting any errors firing back, so nothing to go on.
My first thought is that jquery is unable to match the elements in one list with the other as it just holds an index, not the actual object. This could be totally wrong (please refer back to line 1).
Either way, I need to find a way of getting all elements and segegating them into 2 bits - those with values and those without.
All help appreciated.
function inputLoaded(isPostback) {
if (typeof Page_Validators !== "undefined") {
$scope = $(".active-step:first");
$inputs = $scope.find(inputs);
$cb = $scope.find(checkboxes).filter(":checked");
$rb = $scope.find(radios).filter(":checked");
$sb = $scope.find(selects).filter(function () { return $(this).val() !== "None"; });
$ta = $scope.find(textareas).filter(function () { return $(this).val(); });
$tb = $scope.find(textboxes).filter(function () { return $(this).val(); });
$entries = $cb.add($rb).add($sb).add($ta).add($tb);
// Do things with $entries here
// Get elements that have not got entries
$el = $inputs.remove($entries);
}
}
The not() method can take a jQuery object whose contents will be excluded from the jQuery object you apply it to. It looks exactly like what you're looking for:
// Get elements, excluding entries.
$el = $input.not($entries);

How to avoid using filter twice against the same set of elements

I'm trying to separate two types of inputs into their own jQuery wrapped sets as they need to be processed differently depending on whether the id contain '-add-new-' or not. I know I could do this using filter twice as follows:
var seriesTabInputs = $msSeriesTabs.find('input').filter(function() {
return $(this).attr('id').indexOf('-add-new-') == -1;
});
var addNewTabInputs = $msSeriesTabs.find('input').filter(function() {
return $(this).attr('id').indexOf('-add-new-') >= 0;
});
However filtering twice seems inefficient to me as I know it will require a second loop. Is there a way to avoid this?
Try like below:
var addNewTabInputs = $msSeriesTabs.find('input[id*="-add-new-"]');
var seriesTabInputs = $msSeriesTabs.find('input[id]:not([id*="-add-new-"])');
OR
var addNewTabInputs = $msSeriesTabs.find('input[id*="-add-new-"]');
var seriesTabInputs = $msSeriesTabs.find('input[id]').not(addNewTabInputs);
Just to offer an alternative to using specific selectors, you could iterate through the jQuery set and build the two collections as you go. I don't know that this would be any faster due to the different operations applied to the collections.
var $inputs = $msSeriesTabs.find('input');
var seriesTabInputs = [];
var addNewTabInputs = [];
for (var i = 0; i < $inputs.length ; i += 1)
{
var input = $($inputs[i]);
if ( $(input).attr('id').indexOf('-add-new-') >= 0 )
{ addNewTabInputs.push(input); }
else
{ seriesTabInputs.push(input); }
}
seriesTabInputs = $(seriesTabInputs);
addNewTabInputs = $(addNewTabInputs);
Avoiding filtering twice may not be so crucial unless you are dealing with an enormous amount of elements. Furthermore there is something to be said for the consistency of the code when you filter twice.
That being said there is a way to avoid filtering twice and it may even be instructional; below is some code that can be used to achieve this.
First, we create an empty wrapped set that can be added to, this is achieved by var seriesTabInputs = $(false); Please see this write-up for more information.
Then inside of the filter, we conditionally add to seriesTabInputs but note the way in which we do it: we continually re-assign with seriesTabInputs = seriesTabInputs.add($(this)); If instead you merely call seriesTabInputs.add($(this)) without assigning to seriesTabInput you will wind up with an empty array in the end. Please see the jQuery docs for .add() which gives a similar incorrect example and states that such usage "will not save the added elements, because the .add() method creates a new set".
var seriesTabInputs = $(false);
var addNewTabInputs = $msSeriesTabs.find('input').filter(function() {
if ($(this).attr('id').indexOf('-add-new') >= 0) {
return true;
}
else {
seriesTabInputs = seriesTabInputs.add($(this));
}
});

Build a switch based on array

I want to create a Javascript switch based on an array I'm creating from a query string. I'm not sure how to proceed.
Let's say I have an array like this :
var myArray = ("#general","#controlpanel","#database");
I want to create this...
switch(target){
case "#general":
$("#general").show();
$("#controlpanel, #database").hide();
break;
case "#controlpanel":
$("#controlpanel").show();
$("#general, #database").hide();
break;
case "#database":
$("#database").show();
$("#general, #controlpanel").hide();
break;
}
myArray could contain any amount of elements so I want the switch to be created dynamically based on length of the array. The default case would always be the first option.
The array is created from a location.href with a regex to extract only what I need.
Thanks alot!
#Michael has the correct general answer, but here's a far simpler way to accomplish the same goal:
// Once, at startup
var $items = $("#general,#controlpanel,#database");
// When it's time to show a target
$items.hide(); // Hide 'em all, even the one to show
$(target).show(); // OK, now show just that one
If you really only have an array of selectors then you can create a jQuery collection of them via:
var items = ["#general","#controlpanel","#database"];
var $items = $(items.join(','));
Oh, and "Thanks, Alot!" :)
I think you want an object. Just define keys with the names of your elements to match, and functions as the values. e.g.
var switchObj = {
"#general": function () {
$("#general").show();
$("#controlpanel, #database").hide();
},
"#controlpanel": function () {
$("#controlpanel").show();
$("#general, #database").hide();
},
"#database": function () {
$("#database").show();
$("#general, #controlpanel").hide();
}
}
Then you can just call the one you want with
switchObj[target]();
Granted: this solution is better if you need to do explicitly different things with each element, and unlike the other answers it focused on what the explicit subject of the question was, rather than what the OP was trying to accomplish with said data structure.
Rather than a switch, you need two statements: first, to show the selected target, and second to hide all others.
// Array as a jQuery object instead of a regular array of strings
var myArray = $("#general,#controlpanel,#database");
$(target).show();
// Loop over jQuery list and unless the id of the current
// list node matches the value of target, hide it.
myArray.each(function() {
// Test if the current node's doesn't matche #target
if ('#' + $(this).prop('id') !== target) {
$(this).hide();
}
});
In fact, the first statement can be incorporated into the loop.
var myArray = $("#general,#controlpanel,#database");
myArray.each(function() {
if ('#' + $(this).prop('id') !== target) {
$(this).hide();
}
else {
$(this).show();
}
});
Perhaps you're looking for something like this? Populate myArray with the elements you're using.
var myArray = ["#general","#controlpanel","#database"];
var clone = myArray.slice(0); // Clone the array
var test;
if ((test = clone.indexOf(target)) !== -1) {
$(target).show();
clone.splice(test,1); // Remove the one we've picked up
$(clone.join(',')).hide(); // Hide the remaining array elements
}
here you dont need to explicitly list all the cases, just let the array define them. make sure though, that target exists in the array, otherwise you'll need an if statement.
var target = "#controlpanel";
var items = ["#general","#controlpanel","#database"];
items.splice($.inArray(target, items), 1);
$(target).show();
$(items.join(",")).hide();
items.push(target);

Use Javascript or jQuery to create an array from an array

Assume you have an array:
var arrStateCityAll=['CA_Alameda','CA__Pasadena','CA_Sacramento','NY_Albany','NY_Buffalo','NY_Ithaca']
Is there an easy way using javascript and/or jQuery to filter the arrStateCityAll to get a new array (a subset of arrStateCityAll); something like this:
// return's ['CA_Alameda','CA__Pasadena','CA_Sacramento']
var arrStateCityCA=FilterArray('CA',arrStateCityAll);
Likely you want to do a regex on each item. You can do this with jQuery's grep function.
http://api.jquery.com/jQuery.grep/
You can use javascript's Array.filter.
var arrStateCityAll = ['CA_Alameda','CA__Pasadena','CA_Sacramento','NY_Albany','NY_Buffalo','NY_Ithaca']
var arrStateCityCA = arrStateCityAll.filter( function (element) {
return element.indexOf("CA_") == 0;
});
The mozilla documentation linked to above has a solution for browsers that don't implicitly support filter.
This should work.
var arrStateCityCA = [];
for (var i = 0;i<arrStateCityAll.length;i++){
if (arrStateCityAll[i].substr(0,2) == 'CA'){
arrStateCityCA.push(arrStateCityAll[i]);
}
}
You could use jQuery.grep
var arrStateCityCA =
$.grep(arrStateCityAll,function(el,i){return (el.substring(0,2)=='CA')});
Demo at jsfiddle
To implement you actual FilterArray function as shown in your post you could do
function FilterArray(state,arr){
return $.grep(arr,
function(el,i) {return (el.substring(0,2)==state)}
);
}
This makes a few assumptions.
State is always 2 chars.
State is always the first 2 chars.
And of course remember case-sensitivity (this function is case sensitive) ie 'CA' not equal to 'Ca'.
if you are going to have an undescore between your state and city name, you can split on the underscore and test against the first array value
function getSubSetByState(set,state) {
var result = [];
for(var i=0,l=set.length;i<l;++i) {
if(set[i].split('_')[0] === state) {
result.push(set[i]);
}
}
return result;
}
Use if by giving it the set of places, and then the state you are searching for.

Categories

Resources