"continue" in cursor.forEach()
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()
);
Comments
-
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 over 10 yearsDo you know maybe what could be the "break" then if continue is just "return;".
-
nnnnnn over 10 yearsI don't use MongoDB so haven't read its documentation, but it's possible that
return false;
would be the equivalent ofbreak;
(as it is for a jQuery.each()
loop). Of course whoever implemented MongoDB's.forEach()
may have had other ideas... -
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 almost 8 yearsGood 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 beelementsCollection.toArray().some()
. (Or it looks like a standardwhile
loop using the.hasNext()
and.next()
methods could get the same result usingbreak
. 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 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 usemap
and ignore the result (they should have usedforEach)
. It's semantics, people will have to look twice to know why you are usingsome
when you don't really care about the result -
Rafael Herscovici over 6 yearsThis would iterate the found elements twice, once in the
filter
and the second in theforEach
if it a large collection, it will be very inefficient -
Ramy Tamer over 6 yearsYou 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 asO(n)
. -
Rafael Herscovici over 6 yearsConsidering 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 over 6 years@Andrew great tip, however it's
return true
which will break the some loop -
nnnnnn over 6 yearsNote 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 over 5 yearsgreat tip, thank you :) helped me even 5 years later :) @nnnnnn
-
I have 10 fingers about 2 yearsWell explained!