javascript - Break the loop of an Array looping function (map, forEach, etc.) - Stack Overflow

How can I break (similar to the break statement) from an implicit loop on an array?The Array.prototype

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?

Share Improve this question edited Jun 14, 2023 at 4:50 bignose asked May 28, 2018 at 2:04 bignosebignose 32.3k15 gold badges80 silver badges116 bronze badges 3
  • 1 Array#map is not designed for iteration with side effects (and must not be used for that purpose), it cannot be stopped. Array#forEach cannot be stopped either though. If you need to find something you need to use Array#indexOf or Array#find or 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 for or while or, 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 for loop with a counter. – Scott Marcus Commented May 28, 2018 at 2:25
Add a ment  | 

5 Answers 5

Reset to default 5

You 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, the forEach() 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);
  }
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1741003473a4289794.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信