Pregunta ¿Cómo cortocircuitar Array.forEach como llamar a break?


[1,2,3].forEach(function(el) {
    if(el === 1) break;
});

¿Cómo puedo hacer esto usando el nuevo forEach método en JavaScript? Intenté "devolver", "devolver falso" y "romper". Romper bloqueos y regresar no hace más que continuar la iteración.


1008
2018-04-14 21:57


origen


Respuestas:


No hay una capacidad incorporada para break en forEach. Para interrumpir la ejecución, deberías lanzar una excepción de algún tipo. p.ej.

var BreakException = {};

try {
  [1, 2, 3].forEach(function(el) {
    console.log(el);
    if (el === 2) throw BreakException;
  });
} catch (e) {
  if (e !== BreakException) throw e;
}

Las excepciones de JavaScript no son terriblemente bonitas. Un tradicional for loop puede ser más apropiado si realmente necesita break dentro de eso.

Utilizar Array#some

En cambio, usa Array#some:

[1, 2, 3].some(function(el) {
  console.log(el);
  return el === 2;
});

Esto funciona porque some devoluciones true tan pronto como cualquiera de las devoluciones de llamada, ejecutadas en orden de matriz, regresen true, cortocircuitando la ejecución del resto.

some, es inverso every (que se detendrá en un return false), y forEach son todos los métodos de la Quinta Edición de ECMAScript que deberán agregarse al Array.prototype en buscadores donde faltan.


1404
2018-04-14 22:02



Ahora hay una forma aún mejor de hacer esto en ECMAScript2015 (también conocido como ES6) usando el nuevo para de bucle. Por ejemplo, este código no imprime los elementos de la matriz después del número 5:

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (let el of arr) {
  console.log(el);
  if (el === 5) {
    break;
  }
}

De los documentos:

Ambos para ... en y para ... de declaraciones iterar sobre algo. La principal diferencia entre ellos es en lo que iteran. los para ... en instrucción itera sobre las propiedades enumerables de un objeto, en orden de inserción original. los para ... de La instrucción itera sobre los datos que el objeto iterable define para iterar.

¿Necesita el índice en la iteración? Puedes usar Array.entries():

for (const [index, el] of arr.entries()) {
  if ( index === 5 ) break;
}

165
2017-08-19 16:43



Puedes usar cada método:

[1,2,3].every(function(el) {
    return !(el === 1);
});

para uso antiguo de soporte del navegador:

if (!Array.prototype.every)
{
  Array.prototype.every = function(fun /*, thisp*/)
  {
    var len = this.length;
    if (typeof fun != "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in this &&
          !fun.call(thisp, this[i], i, this))
        return false;
    }

    return true;
  };
}

más detalles aquí.


154
2017-07-19 09:18



Citando de la Documentación MDN de Array.prototype.forEach():

Ahi esta no hay forma de parar o romper un forEach() bucle que no sea   lanzando una excepción. Si necesita tal comportamiento, el .forEach() método es el herramienta incorrecta, use un bucle simple en su lugar. Si está probando los elementos de la matriz para un predicado y necesita un valor de retorno booleano, puede usar every() o some() en lugar.

Para su código (en la pregunta), como lo sugiere @bobince, use Array.prototype.some() en lugar. Se adapta muy bien a tu uso.

Array.prototype.some() ejecuta la función de devolución de llamada una vez para cada elemento presente en la matriz hasta que encuentra una donde la devolución de llamada devuelve un valor de verdad (un valor que se convierte en verdadero cuando se convierte en una Boolean) Si se encuentra ese elemento, some() de inmediato regresa verdadero. De otra manera, some() devuelve falso. la devolución de llamada se invoca solo para los índices de la matriz que tienen valores asignados; no se invoca para los índices que se han eliminado o para los que nunca se han asignado valores.


45
2018-01-06 21:16



Desafortunadamente en este caso será mucho mejor si no usa forEach. En su lugar use un for loop y ahora funcionará exactamente como cabría esperar.

var array = [1, 2, 3];
for (var i = 0; i < array.length; i++) {
  if (array[i] === 1){
    break;
  }
}

38
2017-08-25 20:03



Considere usar jqueryes each método, ya que permite devolver la función de devolución de llamada falsa:

$.each(function(e, i) { 
   if (i % 2) return false;
   console.log(e)
})

Las bibliotecas de Lodash también proporcionan takeWhile método que puede ser encadenado con map / reduce / fold, etc.

var users = [
  { 'user': 'barney',  'active': false },
  { 'user': 'fred',    'active': false },
  { 'user': 'pebbles', 'active': true }
];

_.takeWhile(users, function(o) { return !o.active; });
// => objects for ['barney', 'fred']

// The `_.matches` iteratee shorthand.
_.takeWhile(users, { 'user': 'barney', 'active': false });
// => objects for ['barney']

// The `_.matchesProperty` iteratee shorthand.
_.takeWhile(users, ['active', false]);
// => objects for ['barney', 'fred']

// The `_.property` iteratee shorthand.
_.takeWhile(users, 'active');
// => []

24
2018-04-14 22:06



Si quieres usar Sugerencia de Dean Edward y arroje el error StopIteration para salir del bucle sin tener que detectar el error, puede usar la siguiente función (originalmente de aquí)

// Use a closure to prevent the global namespace from be polluted.
(function() {
  // Define StopIteration as part of the global scope if it
  // isn't already defined.
  if(typeof StopIteration == "undefined") {
    StopIteration = new Error("StopIteration");
  }

  // The original version of Array.prototype.forEach.
  var oldForEach = Array.prototype.forEach;

  // If forEach actually exists, define forEach so you can
  // break out of it by throwing StopIteration.  Allow
  // other errors will be thrown as normal.
  if(oldForEach) {
    Array.prototype.forEach = function() {
      try {
        oldForEach.apply(this, [].slice.call(arguments, 0));
      }
      catch(e) {
        if(e !== StopIteration) {
          throw e;
        }
      }
    };
  }
})();

El código anterior le dará la capacidad de ejecutar código como el siguiente sin tener que hacer sus propias cláusulas try-catch:

// Show the contents until you get to "2".
[0,1,2,3,4].forEach(function(val) {
  if(val == 2)
    throw StopIteration;
  alert(val);
});

Una cosa importante para recordar es que esto solo actualizará la función Array.prototype.forEach si ya existe. Si no existe ya, no lo modificará.


14
2017-07-05 19:32



Desde el ejemplo de tu código, parece Array.prototype.find es lo que estás buscando: Array.prototype.find () y Array.prototype.findIndex () 

[1, 2, 3].find(function(el) {
    return el === 2;
}); // returns 2

13
2018-01-15 20:20



Respuesta corta: uso for...break para esto o cambie su código para evitar la ruptura de forEach. No utilice .some() o .every() emular for...break. Reescribe tu código para evitar for...break loop, o uso for...break. Cada vez que usas estos métodos como for...break Dios alternativo mata al gatito.

Respuesta larga:

.some() y .every() ambos regresan boolean valor, .some() devoluciones true si hay algún elemento por el cual la función pasada devuelve true, cada vuelta false si hay algún elemento por el cual la función pasada devuelve false. Esto es lo que significan esas funciones. Usar funciones para lo que no significa es mucho peor que usar tablas para el diseño en lugar de CSS, porque frustra a todos los que leen el código.

Además, la única forma posible de usar estos métodos es for...break alternativa es hacer efectos secundarios (cambiar algunos vars fuera de .some() función de devolución de llamada), y esto no es muy diferente de for...break.

Entonces, usando .some() o .every() como for...break La alternativa de bucle no está libre de efectos secundarios, esto no es mucho más limpio que for...break, esto es frustrante, así que esto no es mejor.

Siempre puede volver a escribir su código para que no haya necesidad de for...break. Puedes filtrar la matriz usando .filter(), puedes dividir la matriz usando .slice() y así sucesivamente, luego usa .forEach() o .map() para esa parte de la matriz.


8
2017-07-29 12:34