206

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?*/;
})
Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
dy_
  • 6,462
  • 5
  • 24
  • 30
  • 4
    I don't think it's possible, because the native `forEach` function does not offer this feature either. – Felix Kling Jan 08 '12 at 17:56
  • 8
    Normally when using `each` with a closure (in most languages), you want to filter your list first. That way you don't have to worry about breaking from it. Generally speaking, if you need to break early from an iteration, there's probably a different way that you could be doing it. – Rob Hruska Jan 08 '12 at 18:02
  • [Here are](http://stackoverflow.com/questions/1766941/groovy-how-to-exit-each-loop/1767046) [a couple](http://stackoverflow.com/questions/3049790/break-from-groovy-each-closure) related questions for Groovy, where the behavior (inability to conveniently break from an `each` closure) is similar to JavaScript. – Rob Hruska Jan 08 '12 at 18:06
  • @Dmitry_F, as others have noted, you can't do exactly what you're asking. But as I demonstrated, you can use `Array.every` to emulate the behavior you want. – aeskr Jan 08 '12 at 18:17
  • @Rob. Cheers. First comment really helpful. Indeed there was a different way I could have been doing it. – net.uk.sweet Nov 14 '12 at 23:25

11 Answers11

275

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.

William
  • 2,695
  • 1
  • 21
  • 33
aeskr
  • 3,436
  • 1
  • 15
  • 10
71

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"}
Nikhil
  • 905
  • 7
  • 9
  • 1
    this doesn't break the loop - it just filters the array. imagine you have have not 2 but 20.000 items in the array. you log would only output the example you posted but the loop would run 20.000 times :( – Philipp Kyeck Jul 30 '13 at 14:42
  • @pkyeck you are right, may be _.find is better than _.filter as it breaks after the elemnt is found, here is the fiddle : http://jsfiddle.net/niki4810/9K3EV/ – Nikhil Aug 05 '13 at 23:08
  • 2
    I think this answer should be marked as the correct one. `_.find` does exactly what is asked : iterate over the list until the callback returns `true`. – Fabien Quatravaux Aug 15 '13 at 12:29
  • Voted for this answer because the accepted answer (Array.every) won't work on objects, but _.find() will. – matt Aug 30 '13 at 22:15
  • And that's what is recommnded in the docs: It's also good to note that an each loop cannot be broken out of — to break, use _.find instead. – shaharsol Jun 19 '14 at 10:55
17

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

Eric
  • 6,563
  • 5
  • 42
  • 66
Joan
  • 303
  • 2
  • 6
6
_([1,2,3]).find(function(v){
    return v if (v==2);
})
Rockyboy_ruby
  • 233
  • 1
  • 4
  • 15
3

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

czizzy
  • 87
  • 7
3

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

JaredMcAteer
  • 21,688
  • 5
  • 49
  • 65
3

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

grantwparks
  • 1,124
  • 9
  • 13
2

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 {}; 
});
bm_i
  • 568
  • 5
  • 9
  • That only happens with EcmaScript v < 5 as the comparison that underscore does to check if you are returning the empty object in the provided alternative to forEach is only done when the native one is not available. – Alfonso de la Osa Apr 17 '13 at 13:35
2

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

http://underscorejs.org/#each

percebus
  • 799
  • 1
  • 8
  • 21
1

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!

bm_i
  • 568
  • 5
  • 9
  • Love how I get so many down votes for this, and this guy gets a whole load of ups for his http://stackoverflow.com/a/2641374/674720 – bm_i Sep 01 '14 at 13:01
  • 1
    I'm late to the party but note that guy not only said what you suggested, but offered two more alternatives (and more suitable). The only offered the "hack" in case the user by all means want it. You instead only offered the ugly hack. –  Oct 29 '14 at 20:35
-1

worked in my case

var arr2 = _.filter(arr, function(item){
    if ( item == 3 ) return item;
});
aleXela
  • 1,270
  • 2
  • 21
  • 34