how to break the _.each function in underscore.js - javascript

I'm looking for a way to stop iterations of underscore.js _.each() method, but can't find the solution. jQuery .each() can break if you do return false.
Is there a way to stop underscore each()?
_([1,2,3]).each(function(v){
if (v==2) return /*what?*/;
})

You can't break from the each method—it emulates the native forEach method's behavior, and the native forEach doesn't provide to escape the loop (other than throwing an exception).
However, all hope is not lost! You can use the Array.every method. :)
From that link:
every executes the provided callback function once for each element present in the array until it finds one where callback returns a false value. If such an element is found, the every method immediately returns false.
In other words, you could do something convoluted like this (link to JSFiddle):
[1, 2, 3, 4].every(function(n) {
alert(n);
return n !== 3;
});
This will alert 1 through 3, and then "break" out of the loop.
You're using underscore.js, so you'll be pleased to learn that it does provide an every method—they call it every, but as that link mentions, they also provide an alias called all.

Update:
_.find would be better as it breaks out of the loop when the element is found:
var searchArr = [{id:1,text:"foo"},{id:2,text:"bar"}];
var count = 0;
var filteredEl = _.find(searchArr,function(arrEl){
count = count +1;
if(arrEl.id === 1 ){
return arrEl;
}
});
console.log(filteredEl);
//since we are searching the first element in the array, the count will be one
console.log(count);
//output: filteredEl : {id:1,text:"foo"} , count: 1
** Old **
If you want to conditionally break out of a loop, use _.filter api instead of _.each. Here is a code snippet
var searchArr = [{id:1,text:"foo"},{id:2,text:"bar"}];
var filteredEl = _.filter(searchArr,function(arrEl){
if(arrEl.id === 1 ){
return arrEl;
}
});
console.log(filteredEl);
//output: {id:1,text:"foo"}

You can have a look to _.some instead of _.each.
_.some stops traversing the list once a predicate is true.
Result(s) can be stored in an external variable.
_.some([1, 2, 3], function(v) {
if (v == 2) return true;
})
See http://underscorejs.org/#some

_([1,2,3]).find(function(v){
return v if (v==2);
})

Maybe you want Underscore's any() or find(), which will stop processing when a condition is met.

Like the other answers, it's impossible.
Here is the comment about breaker in underscore underscore issue #21

You cannot break a forEach in underscore, as it emulates EcmaScript 5 native behaviour.

I believe if your array was actually an object you could return using an empty object.
_.({1,2,3,4,5}).each(function(v){
if(v===3) return {};
});

It's also good to note that an each loop cannot be broken out of — to
break, use _.find instead.
http://underscorejs.org/#each

Update:
You can actually "break" by throwing an error inside and catching it outside: something like this:
try{
_([1,2,3]).each(function(v){
if (v==2) throw new Error('break');
});
}catch(e){
if(e.message === 'break'){
//break successful
}
}
This obviously has some implications regarding any other exceptions that your code trigger in the loop, so use with caution!

worked in my case
var arr2 = _.filter(arr, function(item){
if ( item == 3 ) return item;
});

Related

Restricted JavaScript Array Pop Polyfill not working

I'm creating a few specific functions for a compiler I'm working on, But certain restrictions within the compiler's nature will prevent me from using native JavaScript methods like Array.prototype.pop() to perform array pops...
So I decided to try and write some rudimentary pseudo-code to try and mimic the process, and then base my final function off the pseudo-code... But my tests seem to fail... based on the compiler's current behavior, it will only allow me to use array.length, array element assignments and that's about it... My code is below...
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
y+1<z?(x[y]=arr[y]):(w=arr[y],arr=x);
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // undefined... should be 2
console.log(Arr); // [-1,0,1,2]... should be [-1,0,1]
I'm trying to mimic the nature of the pop function but can't seem to put my finger on what's causing the function to still provide undefined and the original array... undefined should only return if an initial empty array is sent, just like you would expect with a [].pop() call...
Anyone have any clues as to how I can tailor this code to mimic the pop correctly?
And while I have heard that arr.splice(array.length-1,1)[0]; may work... the compiler is currently not capable of determining splice or similar methods... Is it possible to do it using a variation of my code?
Thanks in advance...
You're really over-thinking [].pop(). As defined in the specs, the process for [].pop() is:
Get the length of the array
If the length is 0
return undefined
If length is more than 0
Get the item at length - 1
Reduce array.length by 1
Return item.
(... plus a few things that the JavaScript engine needs to do behind the scenes like call ToObject on the array or ensure the length is an unsigned 32-bit integer.)
This can be done with a function as simple as the one below, there's not even a need for a loop.
function pop(array) {
var length = array.length,
item;
if (length > 0) {
item = array[length - 1];
array.length -= 1;
}
return item;
}
Edit
I'm assuming that the issue with the compiler is that Array.prototype.pop isn't understood at all. Re-reading your post, it looks like arrays have a pop method, but the compiler can't work out whether the variable is an array or not. In that case, an even simpler version of this function would be this:
function pop(array) {
return Array.prototype.pop.call(array);
}
Try that first as it'll be slightly faster and more robust, if it works. It's also the pattern for any other array method that you may need to use.
With this modification, it works:
http://jsfiddle.net/vxxfxvpL/1/
pop2 = function(arr) {
if(arr.length>0){
for(var w=undefined,x=[],y=0,z=arr.length;y<=z;y++){
if(y+1<z) {
(x[y]=arr[y]);
} else {
(w=arr[y],arr=x);
break;
}
}
}
return w;
}
Arr = [-1,0,1,2];
// Testing...
console.log(pop2(Arr)); // 2
The problem now is to remove the last element. You should construct the original array again without last element. You will have problems with this because you can't modify the original array. That's why this tasks are maded with prototype (Array.prototype.pop2 maybe can help you)

_.findWhere from underscorejs to JQuery

I am trying to implement this code: http://jsfiddle.net/wQysh/351/ in my project.
Everything is fine except for the line:
t = _.findWhere(sc, { id : Number(a.trim()) });
They have used underscorejs and I want to translate this to JQuery without using another lib.
I went through the doc and it stated:
findWhere_.findWhere(list, properties)
Looks through the list and returns the first value that matches all of the key-value pairs listed in properties.
If no match is found, or if list is empty, undefined will be returned.
But still I am confused about this since I am not sure what to return exactly (as first value). Can anyone give me a JQuery alternative to that line?
Thanks in advance..
If you don't the generic nature of _.findWhere() you can use a simple while loop, and compare the id to the numeric value of a (fiddle):
t = 0; // t is used as a counter
aValue = Number(a.trim()); // assign the value to a variable instead of iterating it
while (t < sc.length && sc[t].id !== aValue) { t++; }; // find the index where the id is the as the aValue
t < sc.length && toSet.push(sc[t]); // if t is less the sc.length we found the item in the array
If you need a findWhere without underscore try this gist.
I also used this example in my project. And also needed use JQuery instead of Underscore.
Here is my solution:
t = sc.filter(function (el) { return el.id === a });
It work perfect for me;
If you use number for ids, you can also convert a to integer
t = sc.filter(function (el) { return el.id === parseInt(a, 10) });

Run the same function on each item in a list / array

Goal
I have a working function (JSFiddle). On numerous occasions throughout a script the function runs sequentially. In these instances, there is a lot of repetitious code that I would like to consolidate.
Ideally changing code like this:
functionName("First_item") +
functionName("Second_item") +
functionName("Third_item") +
To something like this:
functionName("First_item", "Second_item", "Third_item");
The function will run for each item in the list so the result is the same but the code more elegant and maintainable.
Notes:
I’m not looking to use any libraries (e.g. jQuery) to accomplish the goal.
Solution
Amit Joki’s answer kindly noted I could use arguments. When I implemented the code, the modified function (JSFiddle) only returned the output string for the first argument / item.
Vanice’s answer pointed out the eventual solution.
Make one string from the output of all arguments / items by concatenating (joining) the output strings within the for loop (with the use of +=).
Return the concatenated output by placing the return outside of the for loop.
Example
Working solution (JSFiddle).
Thanks
Thank you very much to everyone for their time and help on this. I really appreciate it!
Leveraging Javascript's Prototype OOP: You can add an each function to Array's themselves, so every array in your code that will automatically have an inhereted each function.
Array.prototype.each = function(callback){
for (var i = 0; i < this.length; i++){
callback(this[i]);
}
}
Usage:
myArray.each(myCoolFunction)
['something','somethingelse',somethingother'].each(myCoolFunction)
myArray.each( function (item) {
// if your item has a method
item.Something();
// if you'd like to call a function on the item:
doSomething(item);
});
caveats:
Because javascript is an asynchronous language that is interpreted differently across various browsers and inherently handles primitive objects and complex objects differently, it is highly recommended usage of underscore or lodash. You can also make your own, but you'll need ensure the objects passed through will be handled by the functions properly. This may include workarounds or creating special callback functions for different object types that are passed through your each function.
For more information: Is JavaScript a pass-by-reference or pass-by-value language?
Libraries you should seriously consider:
lodash:
https://lodash.com/docs#forEach
_([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
// → logs each number and returns '1,2,3'
_.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
// → logs each number and returns the object (property order is not guaranteed across environments)
underscore:
http://underscorejs.org/#each
_.each([1, 2, 3], alert);
=> alerts each number in turn...
You don't need an array. Just use arguments
function functionName(){
for(var i = 0; i < arguments.length; i++){
// do something with arguments[i];
}
}
and then you can do
functionName("shot_type","shot_height","shot_angle","framed","scene_depth");
P.S #codebox's solution works if supporting legacy IE version isn't a problem. Don't know why he deleted it...so putting it here so it helps. His answer using forEach
["shot_type","shot_height","shot_angle","framed","scene_depth"].forEach(FunctionName);
EDIT: Looking at your Fiddle, you have a return inside the for loop - therefore the function will return after the first iteration. Put the return after the for and concatenate the output to one string.
var output = "";
for(...){
output += description_of_object + ": " + randomly_selected_item_from_object + ".\n";
}
// return it
return output;
With Javascript only:
var actions = ["shot_type","shot_height","shot_angle","framed","scene_depth"];
for (var i = 0; i < actions.length; i++){
FunctionName(actions[i]);
}
With JQuery:
$.each(["shot_type","shot_height","shot_angle","framed","scene_depth"], function(index,value){
FunctionName(value);
});
I haven't tested it but it should work.
To avoide redundancy in code use an array with the values, that you want to pass through the function and call the function in an loop.
var vals=["shot_type","shot_height","shot_angle","framed","scene_depth"];
for(var i=0; i<vals.length; i++)
{
FunctionName(vals[i]);
}
If you want to expand the function (adding another parameter) you can just expand the for-loop and the array structure.
Alternatively you could fill an object with the values and handle this logic in an object. But this would just do a difference on calling the function.

Javascript return position index of "matched" array within array

Is there an alternative, faster method of returning the position/index of part of an array within another array (where multiple values match)? It's called a lot within my pathfinding algorithm so could do with being as fast as possible.
My current function is:
// Haystack can be e.g. [[0,1,278.9],[4,4,22.1212]]
function coordinate_location_in_array(needle,haystack){
for(n in haystack){
if(haystack[n][0]==needle[0] && haystack[n][1]==needle[1]) return n;
}
return false;
}
// Needle of [0,1]: returns 0
// Needle of [4,4]: returns 1
// Needle of [6,7]: returns false
Edit:
I've been messing around a bit and come up with a (rather ghastly) string manipulation-based method (thereby avoiding the costly for loop). I think it's still slightly slower. Could anybody benchmark these methods?
function coordinate_location_in_array(needle,haystack) {
var str1 = ':' + haystack.join(':');
var str2 = str1.replace(':'+needle[0]+','+needle[1],'*').split('*')[0];
if(str2.length == str1.length) return false;
var preceedingElements = str2.match(/:/g);
return preceedingElements!=null?preceedingElements.length:0;
}
Perhaps with some improvements this second method might provide some performance gain?
Edit 2:
Bench marked all 3 described methods using jsperf.com (initial method is fastest):
http://jsperf.com/finding-matched-array-within-array/3
Edit 3:
Just replaced the for(..in..) loop with a for(..;..;..) loop (since I know that the haystack array will never have "gaps") and performance seems to have significantly improved:
function coordinate_location_in_array(needle,haystack){
for(var n=0;n<haystack.length;n++){
if(haystack[n][0]==needle[0] && haystack[n][1]==needle[1]) return n;
}
return false;
}
I've updated the jsperf page to include this latest method.
If the "haystack" isn't sorted then there isn't a way to make it faster. Not knowing how the elements in a collection are ordered makes finding something out of it linear by nature, because you just have to check each thing.
If you are using this function over the same "haystack" over and over, you could sort the collection, and use the sorting to make it quicker to find the "needle" (look up different sorting and search algorithms to find one that fits your need best, such as using binary search to find the "needle" after haystack is sorted.)
i don't know if its faster, but you can do something like:
[1,2,3,4].slice(0,2).toString() == [1,2].toString()
in your case it would be:
function coordinate_location_in_array(needle,haystack){
for(n in haystack){
if(haystack[n].slice(0,2).toString() == needle.toString()) return n
}
return false;
}
Also found this post, which covers comparison of JS arrays: compare-two-arrays-javascript-associative
Cheers
Laidback
Using a for(..;..;..) loop rather than a for(..in..) loop made the biggest difference.
(See Edit 3 at the end of the question)
Seems to me this is just a substring search but with numbers instead of characters being the components of the string. As such, Boyer-Moore could be applicable, especially if your needles and haystacks get big.

jQuery has conditional

Trying to write a conditional with jQuery that basically states, if div.gathering does not contain a.cat-link then do the following. I have tried the following but it doesn't seem to work. Can anyone shed some light on this?
if($("div.gathering:contains('a.cat-link')")){
$(".gathering").append("<a href='#"+data[i]["categories"][0]["category_id"]+"div' class='cat-link' id='"+data[i]["categories"][0]["category_id"]+"' rel='external'>"+data[i]["categories"][0]["category_name"]+"<br />");
}
How about this :
if($("div.gathering").find("a.cat-link").length == 0){
// Conditional statement returned TRUE
}
jQuery selectors return arrays of objects that matched the given selector. This is why we use the length property.
The method that you used - $("div.gathering:contains('a.cat-link')")
would return an empty array and when testing against any object that actually exists (even if it is an empty array) JavaScript will return true.
Example -
var nateArr = [];
if (nateArr){
// Do the dishes...
}else{
// Eat some waffles...
}
If you test this for yourself you will never stop washing those dishes because even though the nateArr contains zero elements it still exists therefore the conditional statement will always return true.
And your fingers will go all wrinkly
try this....
$("div.gathering:not(:contains(a.cat-link))")
.append("<a href='#"+data[i]["categories"][0]["category_id"]+"div' class='cat-link' id='"+data[i]["categories"][0]["category_id"]+"' rel='external'>"+data[i]["categories"][0]["category_name"]+"<br />")
this will only return the div with class gathering which does not have a.cat-link....
hope this helps....

Categories

Resources