Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Documentation - how to short-circuit a loop #224

Closed
zspitz opened this Issue · 11 comments

6 participants

@zspitz

As per the comments on issue 70, there are three ways to short-circuit a loop:
Using _.any:

_([1,2,3,4,5]).any(function (num) {
    return num > 3;
});

Using _.all:

_([1,2,3,4,5]).all(function (num) {
    return num <= 3;
});

Using an exception, and wrapping the _.each call in try...catch:

try {
    _([1,2,3,4,5]).each(function (num) {
        if (num > 3) {
            throw "Loop breaker";
        }
    });
} catch (e) {}

I think this would be a valuable addition to the documentation.

@mrchief

This is helpful to know. However, these aren't straightforward nor intuitive. IMHO, they feel more like a workaround.

Loop 'break' semantically fits better and is more readable.

@kitcambridge

Yes, I'm afraid that neither Underscore's each method nor the Array#forEach implementation defined in ECMAScript 5 provide an explicit way to break loops. Although any and all can be used with side effects to achieve this, I agree that a standard for or while loop with a break statement is significantly faster and more intuitive.

@jashkenas
Owner

If for loops with break are the recommended way to write breaking loops, then we probably shouldn't be encouraging the function alternatives.

@jashkenas jashkenas closed this
@zspitz

Is it the recommended way? Or just significantly faster and more intuitive?
I don't know about faster, but it would certainly be just as intuitive to return the breaker object used internally by the library - Issue 211

@atombender

Prototype.js provides a dummy exception, $break, that the iterating function can catch:

foo.each(function(e) { throw $break; });

Surely Underscore could use the same trick?

@limeblack

I wouldn't throw an exception because of the try/catch overhead and slight awkwardness. As an alternative you could return a 'special' variable.

_(foo).each(function(){  return _.break });

or you could add an additional argument the each loop.

_(foo).each(function(key,val,break){ return break; });

If people really want the try/catch loop then go for it.

@kitgoncharov Are for/while loops really significantly faster, because I had been told/seen otherwise?

@zspitz

@limeblack: See Jeremy Ashkenas' comment on issue 211. Neither of these will have any effect if the native forEach is available.

Just out of curiosity, why is throw $break any more awkward than return _.break?

Just for clarity, we're not talking about having a try/catch within the iterator function passed to _.each, but a try/catch in the _.each function itself. Someone using the library will not have to write a try/catch, only the throw statement.

@atombender

@limeblack: I like your first suggestion.

@limeblack

You could have _.forEach (uses native for speed increase if desired) and _.each(with additional options). I find throw $break awkward, because of just simply personal preference like I said its just what others want. If people really want the try/catch loop then go for it.

I understand it won't be in the code like posted above, but in the underscore.js source. The features are pretty easy to add so ultimately I'm flexible. I use a filter/map/each combo function that combines all of the features.

@atombender

How about adding a method _.break() which internally throws a dummy object (like $break), which is intercepted and therefore never seen by anything outside of Unerscore?

@limeblack

That's cool with me, although using a reserved work like break can throw errors in IE. Another alternative is passing a a default object this that contain some simple methods like break and remove.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.