Drag0
Drag0

Reputation: 8918

"continue" in cursor.forEach()

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().

Upvotes: 421

Views: 361116

Answers (8)

A.Muqtadir
A.Muqtadir

Reputation: 71

// There are two options.

//1. Using 'return' statement with if condition

 elementsCollection.forEach(function(element){
  if (element.shouldBeProcessed == false){
    return; //use this return statement to skip
  }else{
    // This part should be avoided if not neccessary
    doSomeLengthyOperation();
  }
});

//2. If you would like to utilize the key benefit of using functional patterns. filter before applying foreach

    elementsCollection.filter(v => v.shouldBeProcessed == true).forEach(v => {
   doSomeLengthyOperation();// all iterations with "shouldBeProcessed ==false" are skipped
});

Upvotes: 3

GorvGoyl
GorvGoyl

Reputation: 49190

In case you're using classic for loop and don't want to use continue, you can use self-executing function inside it and use return in order to imitate continue behavior:

for (let i = 0; i < 10; i++) {
    (() => {
        if (i > 5) return;
        console.log("no.", i)
    })();
}

console.log("exited for loop")

output:

[LOG]: "no.",  0 
[LOG]: "no.",  1 
[LOG]: "no.",  2 
[LOG]: "no.",  3 
[LOG]: "no.",  4 
[LOG]: "no.",  5 
[LOG]: "exited for loop" 

Upvotes: 0

Ayush
Ayush

Reputation: 576

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.

Upvotes: 7

Akash Kumar Sharma
Akash Kumar Sharma

Reputation: 628

Use continue statement instead of return to skip an iteration in JS loops.

Upvotes: -6

jwerre
jwerre

Reputation: 9584

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)
    }

});

})()

Upvotes: 12

JSON C11
JSON C11

Reputation: 11802

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

elementsCollection.forEach( el => 
  el.shouldBeProcessed && doSomeLengthyOperation()
);

Upvotes: 3

Ramy Tamer
Ramy Tamer

Reputation: 640

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.

Upvotes: 19

nnnnnn
nnnnnn

Reputation: 150020

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();
});

Upvotes: 785

Related Questions