Does the ForEach loop allow us to use break and continue?
I've tried using both but I received an error:
Illegal break/continue statement
If it does allow, how do I use them?
No, it doesn't, because you pass a callback as a return, which is executed as an ordinary function.
Let me be clear:
var arr = [1,2,3];
arr.forEach(function(i) {
console.log(i);
});
// is like
var cb = function(i) {
console.log(i);
// would "break" here do anything?
// would "continue" here do anything?
// of course not.
}
for(var j = 0; j < arr.length; j++) {
cb(arr[j]);
}
All forEach does is call a real, actual function you give to it repeatedly, ignore how it exits, then calls it again on the next element.
In that callback function if you return it will incidentally work like continue in the case of forEach. Because the rest of the function won't execute that time, and it will be called again. There is no analogue of break.
Ruby supports this flexibility, using blocks/procs instead of methods/lambdas.
Per Mozilla's documentation:
Note : There is no way to stop or break a forEach loop. The solution is to use Array.every or Array.some. See example below.
every and some are exactly like forEach, except they pay attention to the return value of the callback. every will break on a falsey value and some will break on a truthy value, since they shortcircuit. They are exactly analogous to && and ||. && tests whether every expression is truthy, || tests for whether some is, which should help you remember how short-circuiting works with every and some. When in doubt, try it.
As already answered, you cannot use continue or break inside a JavaScript Array.prototype.forEach loop. However, there are other options:
Option 1
Use the jQuery .each() function (Reference).
Simply return true to continue, or return false to break the loop.
Option 2
Just use return to continue or throw new Error() to break. I don't necessarily recommend doing it this way, but it's interesting to know that this is possible.
try {
[1, 2, 3, 4].forEach(function(i) {
if (i === 2) {
return; // continue
}
if (i === 3) {
throw new Error(); // break
}
console.log(i);
});
}
catch (e) {
}
The expected result is just 1
Related
So there are some ways to stopping a Generator in for of loop, but how does break send a signal to the Generator(in comparison with return in for-of)?
please consider the code.
As an example, the preceding code just increases a value from 1 to 10 ,and do pause and resume in between.
function *Generator() {
try {
var nextValue;
while (true) {
if (nextValue === undefined) {
nextValue = 1;
}
else {
nextValue++;
}
yield nextValue;
}
}
// cleanup clause
finally {
console.log( "finally has been reached." );
}
}
it loops over it 10 times by using for of:
var it = Generator();// it gets Generator's iterator
for (var v of it) {
console.log(v);
if (v > 9) {
//it.return("stop");//this is another tool for stopping, but it doesn't stop immediately.
break;
console.log("it won't run");//this line won't run
}
}
When it.return() is used by the way, the implementation's clear(it is the main Object and has got the control, but what about the break?);
Iterable objects like your it generator object have a property with the key Symbol.iterator that is a function returning an iterator. Iterators are required to have a .next() method to advance from one item to the next. Then can also optionally have a .return() method, which is called when you break, return, or throw, causing the for..of to stop before it runs to completion. So in your case, break; will automatically call it.return().
The other side of it is that on ES6 generator, .next() makes it resume execution at the currently paused yield, and .return() makes it act like the yield is a return statement, so break inside the loop causes yield nextValue; to behave like return;, which will exit the generator and trigger the finally block.
how does break send a signal to the Generator?
The loop will call the IteratorClose operation, which basically amounts to invoking the iterator's .return() method with no arguments if the iterator object has such a method - which generators do.
This also happens when a throw or return statement in the loop body is evaluated.
When it.return() is used by the way, the implementation is clear
…but horrible. As you found out, it doesn't stop immediately. That's because a method call just advances the generator and gets you some result back from it, but is has nothing to do with your loop. The loop body will just continue to be executed until the loop condition is evaluated again, which then will check the iterator and notice that it's already done.
Trying to create "some" function. i.e., return true if any of the elements in teh array satisfies the condition
function some(array, predicate) {
for (var i = 0; i < array.length; i++) {
if (predicate(array[i]))
return true;
}
return false;
}
console.log(some([NaN, 3, 4], isNaN));
// → true
console.log(some([2, 3, 4], isNaN));
// → false
Question 1: Above is the solution in the book and I'm not sure why "return false" would overwrite "return true" in every case. The above solution seems to suggest that "return false" will be run only if "return true" was never run – WHY??
My solution looked like this.
var some = function(array, condition) {
var answer = false;
for(var i=0; i<array.length; i++) {
if(condition(array[i])) {answer = true};
}
return answer
};
Question 2: Is the book solution better because it takes less memory (ie the "answer" variable)?
Thanks!
return controls where the execution pointer is located for the currently executing script. When return is encountered it moves the execution pointer out of the function, and as a result, the function will stop executing. This means that if return true is executed, return false will not be.
The solution in the book is predicated on the fact that the return keyword used within the body of a for loop will cause the loop to stop execution and return.
Take a look at the at the Interrupt a function section of the return documentation, which states:
The return statement ends function execution and specifies a value to
be returned to the function caller.
In your example, as soon as the condition is matched, the book's solution will no longer continue to iterate through the array and will return (since that's what return does per the definition above).
Let's look at your solution:
var some = function(array, condition) {
var answer = false;
for(var i=0; i<array.length; i++) {
if(condition(array[i])) {answer = true};
}
return answer
};
What happens if the condition is met? You're setting the answer to true, but the loop will continue to check subsequent elements in the array.
Consider the use case where the array is very large and you want to find a specific value within the array.
Which do you think would be more efficient: the one that stops after it finds the match or the one that returns the match only after having looking at all elements?
In this case, the answer is the latter. Don't keep looping through all elements if you've already found what you need.
When the execution hits a return statement, it leaves the current function regardless of which block it was in within that function.
As soon as an array element corresponds to the predicate, it returns true. If the execution exits the for loop normally, it means not a single value was valid, so we can return false.
The book's answer is better, not because it doesn't create an extra variable (that doesn't matter), but because it doesn't check all variables every time. As soon as an array element is valid, it exits the function.
You can use the built in method of Array, some():
var test = [NaN, 3, 4].some(isNaN); // true
What that function does is
function some(array, predicate) { // return a boolean value (true, false)
// as soon an Array key matches `predicate`
for (var i = 0; i < array.length; i++) {
if (predicate(array[i])) return true; // if Array key isNaN exit function as `true`
}
// We're 1. already out of the function or 2. all keys are looped.
// This function needs to return a boolean so exit with `false`
// since all Array keys are Numbers
return false;
}
the above explains it all. Remember the part that says:
"return a boolean value (true, false) as soon an Array key matches predicate"
In your example, you're not exiting the function, so if you passed for example an
array of 1000 keys, every single key would be looped, which the function is not meant to do since it's purpose it to:
Return (exit) as true if at least one key matches the predicate condition
looping all other keys is useless since we already discovered that one key matches.
Yes, var consumes interpreter memory (nothing to worry about).
Here is a piece of a javascript practice problem that I am working on:
function combo(str){
var splitStr=str.split("");
for (var i=0; i<splitStr.length; i++){
return splitStr[i];
}
}
console.log(combo("dog"));
Why won't the loop return all three letters? It prints "d" and stops. If I take the loop out of the function, it works.
What am I doing wrong? Please explain.
return ends the invocation of the function, giving you back whatever is after that return
Your code structure looks like you wanted it to act like yield but ES5- does not support this so you can't use it (yet), and you would still have other problems (i.e. you'd be logging just the first generated result)
Instead, pass your console.log (bound to console) into a the loop;
function combo(str, callback){
var splitStr=str.split("");
for (var i=0; i<splitStr.length; i++){
callback(splitStr[i]);
}
}
combo('dog', console.log.bind(console));
With return you will end the function and come out of it with that data, next things wont be called
You can return an array instead like this.
function combo(str){
return splitStr=str.split("");
}
console.log(combo("dog"));
You are returning from inside the for loop and this ceases the function's execution. If you want to return each letter of the array, just return the split array itself:
function combo(str) {
return str.split("");
}
The behavior you seem to want is somehow achievable using generator function (function*).
function* combo(){
var splitStr = str.split("");
for (var i=0; i < splitStr.length; i++){
yield splitStr[i];
}
}
Then you'd invoke it like this:
var generator = combo("dog");
console.log(generator.next()) //d
console.log(generator.next()) //o
console.log(generator.next()) //g
Please note, however, that so far only few browser support this feature.
return stops the execution of the function. The rest of the loop is not happening.
'return' within the loop ends the loop and returns the first value, since it allowed the loop to run only once.
putting the 'return' statement outside of the scope {} of the loop will allow the loop to continue to run/repeat.
There is continue; to stop the loop and move to the next loop
There is break; to stop the loop and move to the end of the loop
Isn't there some kind of start; that stop the loop and move to the beginning of the loop?
I know it is easy to achieve all of these three actions by just modifying the value of i, but I always try to look for already built-it functions.
Resetting the value of your loop variable to the initial value then calling continue is as close as you'll get.
For example:
for(var i=0; i<20; i++) {
if(somecondition) {
i=-1; continue;
}
}
No - there is no keyword or other way to do it automatically.
As you already mentioned you can just modify the loop condition variable(s) within your loop. Easy if it's a simple i counter, but of course you may have more initialisation to do than just a simple counter.
Or you can do something like the following:
restartLoop:
while (true) {
for (var i=0, j=100000, x="test"; i < 1000; i++, j--, x+= ".") {
if (/*some condition, want to restart the loop*/)
continue restartLoop;
}
break;
}
The continue restartLoop will jump back out to continue with the next iteration of the while loop, which then immediately starts the for loop from the beginning including all of the initialisation code. If the for exits normally the break statement after it will break out of the containing while loop.
I don't really recommend doing this in a general sense, but if your loop initialisation process was really complicated it could be worth it because then you wouldn't need to repeat it all inside the loop. If you needed to do even more initialisation than fits nicely in the for statement's initialisation expression you can easily put it just before the for loop inside the while and it will all be re-run...
If you want to avoid jumps or the equivalent of goto statements that many of us have been trained to avoid, you could use a local function for the loop and a test on the return value to see if you should just call it again:
function doItAll() {
// put state variables other than the actual loop control here
function doTheLoop() {
for(var i=0; i<20; i++) {
if (somecondition) {
return(true); // run the loop again
}
}
return(false); // done running the loop
}
while (doTheLoop()) {}
// do some things after the loop
}
No. (Just to rule out a "I just haven't heard of it, either" - it isn't mentioned at https://developer.mozilla.org/en/JavaScript/Reference/Statements.)
continue works by simply skipping the rest of the loop body. break works by skipping the rest of the loop body and then ending the loop. A start function would have to somehow "rewind" the state of the program - but not all of the state of the program, since presumably you don't want to lose what you did, either - to where it was when the loop began, which is not something that any programming language that I have seen provides.
You could have the loop in a function that calls itself recursively:
function loopIt(numTimes) {
if (numTimes < 3) {
for (x = 0; x < 20; x++) {
if (x == 5) {
loopIt(numTimes+1);
break;
}
}
}
}
You can obviously change the conditions to fit you logic as the above is a simple example.
In this very contrived example, I have an array with 3 elements that I'm looping over using the .each() method.
var vals = $w('foo bar baz');
vals.each( function(val) {
alert(val);
if( val == 'bar' ) {
//This exits function(val)
//but still continues with the .each()
return;
}
});
I can easily return out of the function being called by .each() if I need to.
My question is, how can I break out of the .each() loop from inside the function that .each() is calling?
if( val == 'bar' ) {
throw $break;
}
It's documented at the same page you linked. It's an exception specially handled by the each function. When thrown, it prevents your function from being called on further elements.
Your are correct, and Prototype has created an object ($break) that can be thrown from the each-function to enable this functionality. According to the Prototype API docs:
Regular loops can be short-circuited in JavaScript using the break and continue statements. However, when using iterator functions, your code is outside of the loop scope: the looping code happens behind the scene.
In order to provide you with equivalent (albeit less optimal) functionality, Prototype provides two global exception objects, $break and $continue. Throwing these is equivalent to using the corresponding native statement in a vanilla loop. These exceptions are properly caught internally by the each method.
Also, note that the $continue object has been deprecated, and to simulate a continue-statement, use a vanilla return statement instead.
Code example:
var result = [];
$R(1,10).each(function(n) {
if (0 == n % 2)
return; // this equals continue
if (n > 6)
throw $break;
result.push(n);
});
// result -> [1, 3, 5]
You can read more about the each-function here: http://www.prototypejs.org/api/enumerable/each
Based on the documentation for .each() that you linked to, you should use a throw $break; statement, this should cause further iterations to cease.
Simply returning will cause the iterator to continue to the next one.
From that page you linked to, isn't the correct way
if(val == 'bar')
{
throw $break;
}
?