Reputation: 8918
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
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
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
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
Reputation: 628
Use continue statement instead of return to skip an iteration in JS loops.
Upvotes: -6
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
Reputation: 11802
Making use of JavaScripts short-circuit evaluation. If el.shouldBeProcessed
returns true, doSomeLengthyOperation
elementsCollection.forEach( el =>
el.shouldBeProcessed && doSomeLengthyOperation()
);
Upvotes: 3
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
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