"continue" in cursor.forEach()

272,993

Solution 1

Each iteration of the forEach() will call the function that you have supplied. To stop further processing within any given iteration (and continue with the next item) you just have to return from the function at the appropriate point:

elementsCollection.forEach(function(element){
  if (!element.shouldBeProcessed)
    return; // stop processing this iteration

  // This part will be avoided if not neccessary
  doSomeLengthyOperation();
});

Solution 2

In my opinion the best approach to achieve this by using the filter method as it's meaningless to return in a forEach block; for an example on your snippet:

// Fetch all objects in SomeElements collection
var elementsCollection = SomeElements.find();
elementsCollection
.filter(function(element) {
  return element.shouldBeProcessed;
})
.forEach(function(element){
  doSomeLengthyOperation();
});

This will narrow down your elementsCollection and just keep the filtred elements that should be processed.

Solution 3

Here is a solution using for of and continue instead of forEach:


let elementsCollection = SomeElements.find();

for (let el of elementsCollection) {

    // continue will exit out of the current 
    // iteration and continue on to the next
    if (!el.shouldBeProcessed){
        continue;
    }

    doSomeLengthyOperation();

});

This may be a bit more useful if you need to use asynchronous functions inside your loop which do not work inside forEach. For example:


(async fuction(){

for (let el of elementsCollection) {

    if (!el.shouldBeProcessed){
        continue;
    }

    let res;

    try {
        res = await doSomeLengthyAsyncOperation();
    } catch (err) {
        return Promise.reject(err)
    }

});

})()

Solution 4

The simple answer is putting a return statement inside of the forEach loop will do the work for you as @nnnnnn said,

elementsCollection.forEach(function(element){
  if (!element.shouldBeProcessed)
    return; // stop processing this iteration

  // This part will be avoided if not neccessary
  doSomeLengthyOperation();
});

but if you want a deep answer to that question then just be with me.

Assuming that you don't know the implementation of forEach loop then take a look at the following implementation of forEach loop which is exactly the one specified in ECMA-262, 5th edition for forEach loop.

Source Array.prototype.forEach() - JavaScript | MDN

if (!Array.prototype['forEach']) {

  Array.prototype.forEach = function(callback, thisArg) {

    if (this == null) { throw new TypeError('Array.prototype.forEach called on null or undefined'); }

    var T, k;
    // 1. Let O be the result of calling toObject() passing the
    // |this| value as the argument.
    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get() internal
    // method of O with the argument "length".
    // 3. Let len be toUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If isCallable(callback) is false, throw a TypeError exception.
    // See: https://es5.github.com/#x9.11
    if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); }

    // 5. If thisArg was supplied, let T be thisArg; else let
    // T be undefined.
    if (arguments.length > 1) { T = thisArg; }

    // 6. Let k be 0
    k = 0;

    // 7. Repeat, while k < len
    while (k < len) {

      var kValue;

      // a. Let Pk be ToString(k).
      //    This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty
      //    internal method of O with argument Pk.
      //    This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal
        // method of O with argument Pk.
        kValue = O[k];

        // ii. Call the Call internal method of callback with T as
        // the this value and argument list containing kValue, k, and O.
        callback.call(T, kValue, k, O);
      }
      // d. Increase k by 1.
      k++;
    }
    // 8. return undefined
  };
}

You really don't need to understand every line of the above code because what we are interested in is the while loop,

while (k < len) {

      var kValue;

      // a. Let Pk be ToString(k).
      //    This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the HasProperty
      //    internal method of O with argument Pk.
      //    This step can be combined with c
      // c. If kPresent is true, then
      if (k in O) {

        // i. Let kValue be the result of calling the Get internal
        // method of O with argument Pk.
        kValue = O[k];

        // ii. Call the Call internal method of callback with T as
        // the this value and argument list containing kValue, k, and O.
        callback.call(T, kValue, k, O);
      }
      // d. Increase k by 1.
      k++;
    }

If you notice then there is a statement callback.call(T, KValue, K, O) again we are not interested in the arguments given to the call() method here but what we are really interested in is the callback binding which is a function that you give to your forEach loop in javascript. See the call method just calls the object (javascript function) it is called on with a this value and arguments provided individually.

If you don't understand what call is then take a look at Function.prototype.Call() - JavaScript | MDN.

Just think about this if at any point your function that is callback in this case returns at any point the loop will be updated as usual. The loop doesn't care about if the callback function has performed each and every step given to it or not if the control has returned to the loop the loop has to do its job. Every time the loop is updated the callback is called with new set of values as you can see there T, KValue, K, O are changing every time the loop updates, so if at any point you return from your function i.e., callback you are just handing the control to the loop you are called in no matter at what point you return from your function, if you want to skip some operations inside of your function at a given condition then just put the return statement before those statements you want to skip.

That is how you skip a iteration inside of a forEach loop.

Solution 5

Making use of JavaScripts short-circuit evaluation. If el.shouldBeProcessed returns true, doSomeLengthyOperation

elementsCollection.forEach( el => 
  el.shouldBeProcessed && doSomeLengthyOperation()
);
Share:
272,993
Drag0
Author by

Drag0

Machine Learning, Data Science, Android, nodejs

Updated on July 28, 2021

Comments

  • Drag0
    Drag0 almost 3 years

    I'm building an app using meteor.js and MongoDB and I have a question about cursor.forEach(). I want to check some conditions in the beginning of each forEach iteration and then skip the element if I don't have to do the operation on it so I can save some time.

    Here is my code:

    // Fetch all objects in SomeElements collection
    var elementsCollection = SomeElements.find();
    elementsCollection.forEach(function(element){
      if (element.shouldBeProcessed == false){
        // Here I would like to continue to the next element if this one 
        // doesn't have to be processed
      }else{
        // This part should be avoided if not neccessary
        doSomeLengthyOperation();
      }
    });
    

    I know I could turn cursor to array using cursor.find().fetch() and then use regular for-loop to iterate over elements and use continue and break normally but I'm interested if there is something similiar to use in forEach().

  • Drag0
    Drag0 over 10 years
    Do you know maybe what could be the "break" then if continue is just "return;".
  • nnnnnn
    nnnnnn over 10 years
    I don't use MongoDB so haven't read its documentation, but it's possible that return false; would be the equivalent of break; (as it is for a jQuery .each() loop). Of course whoever implemented MongoDB's .forEach() may have had other ideas...
  • Andrew
    Andrew almost 9 years
    @Drag0 You can use .some() as a replacement for .forEach(), which enables you to return false to break the loop.
  • nnnnnn
    nnnnnn almost 8 years
    Good point @Andrew, except that a MongoDB cursor object is not an array and thus doesn't have a .some() method, so it would need to be elementsCollection.toArray().some(). (Or it looks like a standard while loop using the .hasNext() and .next() methods could get the same result using break. I say "looks like" because I still haven't used MongoDB, but just now I was looking at the doco out of curiosity.)
  • Ruan Mendes
    Ruan Mendes over 7 years
    @Andrew You can use some, just be aware that you are misusing (or creatively using) a function that was intended to tell if any of the elements match the condition. Kind of like when I see people use map and ignore the result (they should have used forEach). It's semantics, people will have to look twice to know why you are using some when you don't really care about the result
  • Rafael Herscovici
    Rafael Herscovici over 6 years
    This would iterate the found elements twice, once in the filter and the second in the forEach if it a large collection, it will be very inefficient
  • Ramy Tamer
    Ramy Tamer over 6 years
    You are right, but I don't think it's a big deal as the time complexity of this would be O(2n) which can be considered as O(n).
  • Rafael Herscovici
    Rafael Herscovici over 6 years
    Considering SO is being used by others, not just the OP, posting a solution just for the purpose of posting it, is creating more harm than good. The answer above does it in one iteration and is the right way to do it.
  • daviestar
    daviestar over 6 years
    @Andrew great tip, however it's return true which will break the some loop
  • nnnnnn
    nnnnnn over 6 years
    Note that the OP's collection is not an array, it's a Mongo DB cursor object, which doesn't seem to have a .filter() method, so you'd have to call its .toArray() method before you could .filter()
  • addi2113
    addi2113 over 5 years
    great tip, thank you :) helped me even 5 years later :) @nnnnnn
  • I have 10 fingers
    I have 10 fingers about 2 years
    Well explained!