admin管理员组文章数量:1130349
How can I break (similar to the break statement) from an implicit loop on an array?
The Array.prototype.map, Array.prototype.forEach, etc. functions imply a loop over the elements of the array. I want to conditionally break that loop early.
This contrived example:
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
colours.map(item => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
break;
}
});
How can I break (similar to the break statement) from an implicit loop on an array?
The Array.prototype.map, Array.prototype.forEach, etc. functions imply a loop over the elements of the array. I want to conditionally break that loop early.
This contrived example:
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
colours.map(item => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
break;
}
});
causes a SyntaxError: unlabeled break must be inside loop or switch.
How can I break the loop the same way a break statement would?
-
1
Array#mapis not designed for iteration with side effects (and must not be used for that purpose), it cannot be stopped.Array#forEachcannot be stopped either though. If you need to find something you need to useArray#indexOforArray#findor any other lookup method. – zerkms Commented May 28, 2018 at 2:08 -
You can't break a map. You'll have to use a stoppable method, such as
fororwhileor, as @zerkms suggested, you could search for your item without iterating at all. – tao Commented May 28, 2018 at 2:09 -
You can't because those methods don't allow for it. But, if you really need that functionality, you should just use a
forloop with a counter. – Scott Marcus Commented May 28, 2018 at 2:25
5 Answers
Reset to default 5You can't do it using a regular way. You can emulate the break behaviour by remembering whether the loop is "broken". The lack of this solution is that the loop actually continues (though the iteration logic is skipped).
let isBroken = false;
colours.map(item => {
if (isBroken) {
return;
}
if (item.startsWith("y")) {
console.log("The yessiest colour!");
isBroken = true;
return;
}
});
The best solution for your example is to use a plain for loop.
for (colour of colours) {
if (colour.startsWith("y")) {
console.log("The yessiest colour!");
break;
}
}
Also you can use a dirty way to actually stop a map loop.
colours.map((item, index, array) => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
array.splice(0, index);
}
});
// The colours array will be modified after this loop
Although forEach is designed to run some function that doesn't change the array (i.e. it is designed to do some other side effect for each item), it is explicitly documented to have no way of breaking the loop.
From the MDN documentation for forEach:
There is no way to stop or break a
forEach()loop other than by throwing an exception. If you need such behavior, theforEach()method is the wrong tool.
So, despite forEach being designed for side-effects, there is no normal access to the control structure of the loop.
Because Array.prototype.map and Array.prototype.reduce are intended to generate a new value, they are not designed for side effects like breaking early. The documentation doesn't appear to say that explicitly.
Possible alternatives to try: re-write the code to use Array.prototype.some or Array.prototype.every. These are explicitly documented to terminate the loop early when their condition is known (when some would return true, or when every would return false).
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
colours.some(item => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
return true;
}
});
Array#map, Array#forEach and so on have never been designed to be stopped. That would feel odd since the intent of map as well forEach really is to iterate over all items.
Also i don't think it is possible to notify the caller that a break event has occurred, since it is within a function that is not an integral part of the original loop.
So let's see at a custom method that stops the loop at the first occurrence of true without returning the matching value itself:
Object.defineProperty(Array.prototype, 'untilTrue', {
enumerable: false,
value: function(lambda) {
for(let i in this) {
if(lambda.call(this, this[i])) return;
}
}
});
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
colours.untilTrue(item => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
return true;
}
console.log(item);
});
Comparing this custom untilTrue to the use of Array#find:
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
colours.find(item => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
return true;
}
return false;
});
The only notable difference is that untilTrue doesn't return the matching item - Array#find does that in addition to call lambda.
So in general i would just stick to Array#find to keep the code neat and clean and use it like this:
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
if(colours.find(item => item.startsWith("y")) !== undefined) {
console.log("The yessiest colour!");
}
This stops the loop at the first match (and returns the matching element). Also note that you have to pare against undefined - in case you were searching for a false or null value, the check would never evaluate to true if just pared to true.
you can throw an exception if your only option is to use Array.forEach
Refer this:
How to short circuit Array.forEach like calling break?
There are other methods available which can solve your purpose too. For example you can use method: Array.prototype.some() if you want to check some condition and break the loop based on that condition.
Example can be referenced here:
https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
you can create your custom forEach method in these ways
Array.prototype._forEach = function (callback) {
let _break = false;
const breaker = () => {
_break = true;
};
for (let index = 0; index < this.length; index++) {
if (_break) break;
callback(this[index], index, breaker);
}
};
// Example for usage:
const letters = ["a", "b", "c", "d", "e", "f", "g"];
letters._forEach((data, index, breaker) => {
if (data === "c") return; // continue role
if (data === "e") return breaker(); // break role
console.log(`log ${index}: ${data}`);
});
/**
* result:
* log 0: a
* log 1: b
* log 3: d
*/
or you can create top custom forEach method by creating
function forEach(items, callback) {
let _break = false;
const breaker = () => {
_break = true;
};
for (let index = 0; index < items.length; index++) {
if (_break) break;
callback(items[index], index, breaker);
}
}
How can I break (similar to the break statement) from an implicit loop on an array?
The Array.prototype.map, Array.prototype.forEach, etc. functions imply a loop over the elements of the array. I want to conditionally break that loop early.
This contrived example:
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
colours.map(item => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
break;
}
});
How can I break (similar to the break statement) from an implicit loop on an array?
The Array.prototype.map, Array.prototype.forEach, etc. functions imply a loop over the elements of the array. I want to conditionally break that loop early.
This contrived example:
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
colours.map(item => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
break;
}
});
causes a SyntaxError: unlabeled break must be inside loop or switch.
How can I break the loop the same way a break statement would?
-
1
Array#mapis not designed for iteration with side effects (and must not be used for that purpose), it cannot be stopped.Array#forEachcannot be stopped either though. If you need to find something you need to useArray#indexOforArray#findor any other lookup method. – zerkms Commented May 28, 2018 at 2:08 -
You can't break a map. You'll have to use a stoppable method, such as
fororwhileor, as @zerkms suggested, you could search for your item without iterating at all. – tao Commented May 28, 2018 at 2:09 -
You can't because those methods don't allow for it. But, if you really need that functionality, you should just use a
forloop with a counter. – Scott Marcus Commented May 28, 2018 at 2:25
5 Answers
Reset to default 5You can't do it using a regular way. You can emulate the break behaviour by remembering whether the loop is "broken". The lack of this solution is that the loop actually continues (though the iteration logic is skipped).
let isBroken = false;
colours.map(item => {
if (isBroken) {
return;
}
if (item.startsWith("y")) {
console.log("The yessiest colour!");
isBroken = true;
return;
}
});
The best solution for your example is to use a plain for loop.
for (colour of colours) {
if (colour.startsWith("y")) {
console.log("The yessiest colour!");
break;
}
}
Also you can use a dirty way to actually stop a map loop.
colours.map((item, index, array) => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
array.splice(0, index);
}
});
// The colours array will be modified after this loop
Although forEach is designed to run some function that doesn't change the array (i.e. it is designed to do some other side effect for each item), it is explicitly documented to have no way of breaking the loop.
From the MDN documentation for forEach:
There is no way to stop or break a
forEach()loop other than by throwing an exception. If you need such behavior, theforEach()method is the wrong tool.
So, despite forEach being designed for side-effects, there is no normal access to the control structure of the loop.
Because Array.prototype.map and Array.prototype.reduce are intended to generate a new value, they are not designed for side effects like breaking early. The documentation doesn't appear to say that explicitly.
Possible alternatives to try: re-write the code to use Array.prototype.some or Array.prototype.every. These are explicitly documented to terminate the loop early when their condition is known (when some would return true, or when every would return false).
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
colours.some(item => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
return true;
}
});
Array#map, Array#forEach and so on have never been designed to be stopped. That would feel odd since the intent of map as well forEach really is to iterate over all items.
Also i don't think it is possible to notify the caller that a break event has occurred, since it is within a function that is not an integral part of the original loop.
So let's see at a custom method that stops the loop at the first occurrence of true without returning the matching value itself:
Object.defineProperty(Array.prototype, 'untilTrue', {
enumerable: false,
value: function(lambda) {
for(let i in this) {
if(lambda.call(this, this[i])) return;
}
}
});
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
colours.untilTrue(item => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
return true;
}
console.log(item);
});
Comparing this custom untilTrue to the use of Array#find:
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
colours.find(item => {
if (item.startsWith("y")) {
console.log("The yessiest colour!");
return true;
}
return false;
});
The only notable difference is that untilTrue doesn't return the matching item - Array#find does that in addition to call lambda.
So in general i would just stick to Array#find to keep the code neat and clean and use it like this:
const colours = ["red", "orange", "yellow", "green", "blue", "violet"];
if(colours.find(item => item.startsWith("y")) !== undefined) {
console.log("The yessiest colour!");
}
This stops the loop at the first match (and returns the matching element). Also note that you have to pare against undefined - in case you were searching for a false or null value, the check would never evaluate to true if just pared to true.
you can throw an exception if your only option is to use Array.forEach
Refer this:
How to short circuit Array.forEach like calling break?
There are other methods available which can solve your purpose too. For example you can use method: Array.prototype.some() if you want to check some condition and break the loop based on that condition.
Example can be referenced here:
https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
you can create your custom forEach method in these ways
Array.prototype._forEach = function (callback) {
let _break = false;
const breaker = () => {
_break = true;
};
for (let index = 0; index < this.length; index++) {
if (_break) break;
callback(this[index], index, breaker);
}
};
// Example for usage:
const letters = ["a", "b", "c", "d", "e", "f", "g"];
letters._forEach((data, index, breaker) => {
if (data === "c") return; // continue role
if (data === "e") return breaker(); // break role
console.log(`log ${index}: ${data}`);
});
/**
* result:
* log 0: a
* log 1: b
* log 3: d
*/
or you can create top custom forEach method by creating
function forEach(items, callback) {
let _break = false;
const breaker = () => {
_break = true;
};
for (let index = 0; index < items.length; index++) {
if (_break) break;
callback(items[index], index, breaker);
}
}
本文标签: javascriptBreak the loop of an Array looping function (mapforEachetc)Stack Overflow
版权声明:本文标题:javascript - Break the loop of an Array looping function (map, forEach, etc.) - Stack Overflow 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://it.en369.cn/questions/1741003472a1811536.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。


发表评论