"continue" in cursor.forEach()

JavascriptMongodbForeachMeteor

Javascript Problem Overview


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

Javascript Solutions


Solution 1 - Javascript

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 - Javascript

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 - Javascript

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 - Javascript

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

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

Solution 5 - Javascript

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 6 - Javascript

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" 

Solution 7 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDrag0View Question on Stackoverflow
Solution 1 - JavascriptnnnnnnView Answer on Stackoverflow
Solution 2 - JavascriptRamy TamerView Answer on Stackoverflow
Solution 3 - JavascriptjwerreView Answer on Stackoverflow
Solution 4 - JavascriptJSON C11View Answer on Stackoverflow
Solution 5 - JavascriptAyushView Answer on Stackoverflow
Solution 6 - JavascriptGorvGoylView Answer on Stackoverflow
Solution 7 - JavascriptAkash Kumar SharmaView Answer on Stackoverflow