javascript - es6 - Corto circuito Array.forEach como llamar a break
javascript foreach object (28)
Ahora hay una forma aún mejor de hacerlo en ECMAScript2015 (también conocido como ES6) utilizando el nuevo bucle for . 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 la documentación:
Tanto para ... en y para ... de declaraciones iterar sobre algo. La principal diferencia entre ellos está en lo que se repite. La instrucción for ... in itera sobre las propiedades enumerables de un objeto, en el orden de inserción original. La sentencia for ... de iteración sobre datos que el objeto iterable define para ser iterada.
¿Necesitas el índice en la iteración? Puedes usar Array.entries()
:
for (const [index, el] of arr.entries()) {
if ( index === 5 ) break;
}
[1,2,3].forEach(function(el) {
if(el === 1) break;
});
¿Cómo puedo hacer esto usando el nuevo método forEach
en JavaScript? He intentado return;
, return false;
y break
break
choques y return
no hace más que continuar la iteración.
Como se mencionó anteriormente, no puedes romper .forEach()
.
Aquí hay una forma ligeramente más moderna de hacer un foreach con los iteradores ES6. Le permite obtener acceso directo al index
/ value
al iterar.
const array = [''one'', ''two'', ''three''];
for (const [index, val] of array.entries()) {
console.log(''item:'', { index, val });
if (index === 1) {
console.log(''break!'');
break;
}
}
Salida:
item: { index: 0, val: ''one'' }
item: { index: 1, val: ''two'' }
break!
Campo de golf
Considere utilizar each
método de jquery
, ya que permite devolver una función de devolución de llamada interna falsa:
$.each(function(e, i) {
if (i % 2) return false;
console.log(e)
})
Las bibliotecas de Lodash también proporcionan el método takeWhile
que se puede encadenar 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'');
// => []
Cotización de la documentación MDN de Array.prototype.forEach()
:
No hay forma de detener o romper un bucle
forEach()
no sea lanzar una excepción. Si necesita tal comportamiento, el método.forEach()
es la herramienta incorrecta , use un bucle plano en su lugar. Si está probando los elementos de la matriz para un predicado y necesita un valor de retorno booleano, puede usarevery()
osome()
lugar.
Para su código (en la pregunta), como lo sugiere @bobince, use some() lugar. Se adapta muy bien a tu caso de 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 uno en el que la devolución de llamada devuelve un valor verdadero (un valor que se convierte en verdadero cuando se convierte aBoolean
). Si se encuentra un elemento de este tipo,some()
devuelve inmediatamente true. De lo contrario,some()
devuelve false. 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 que nunca se han asignado valores.
De acuerdo con @bobince, upvoted.
Además, para su información:
Prototype.js tiene algo para este propósito:
<script type="text/javascript">
$$(''a'').each(function(el, idx) {
if ( /* break condition */ ) throw $break;
// do something
});
</script>
$break
será capturado y manejado por Prototype.js internamente, rompiendo el ciclo "cada uno" pero no generando errores externos.
Ver Prototype.JS API para más detalles.
jQuery también tiene una forma, simplemente devuelve falso en el controlador para interrumpir el ciclo temprano:
<script type="text/javascript">
jQuery(''a'').each( function(idx) {
if ( /* break condition */ ) return false;
// do something
});
</script>
Ver jQuery API para más detalles.
Desafortunadamente, en este caso será mucho mejor si no usa forEach
. En su lugar, use un bucle for
regular y ahora funcionará exactamente como lo esperaría.
var array = [1, 2, 3];
for (var i = 0; i < array.length; i++) {
if (array[i] === 1){
break;
}
}
En su ejemplo de código, parece que Array.prototype.find
es lo que está buscando: Array.prototype.find() y Array.prototype.findIndex()
[1, 2, 3].find(function(el) {
return el === 2;
}); // returns 2
Encontré esta solución en otro sitio. Puedes envolver el forEach en un escenario de prueba / captura.
if(typeof StopIteration == "undefined") {
StopIteration = new Error("StopIteration");
}
try {
[1,2,3].forEach(function(el){
alert(el);
if(el === 1) throw StopIteration;
});
} catch(error) { if(error != StopIteration) throw error; }
Más detalles aquí: http://dean.edwards.name/weblog/2006/07/enum/
Este es un bucle for, pero mantiene la referencia del objeto en el bucle al igual que un forEach () pero se puede romper.
var arr = [1,2,3];
for (var i = 0, el; el = arr[i]; i++) {
if(el === 1) break;
}
Esto es algo que se me ocurrió para resolver el problema ... Estoy bastante seguro de que soluciona el problema que tenía el autor de la pregunta original:
Array.prototype.each = function(callback){
if(!callback) return false;
for(var i=0; i<this.length; i++){
if(callback(this[i], i) == false) break;
}
};
Y luego lo llamarías usando:
var myarray = [1,2,3];
myarray.each(function(item, index){
// do something with the item
// if(item != somecondition) return false;
});
Devolver falso dentro de la función de devolución de llamada causará una interrupción. Déjame saber si eso realmente no funciona.
Esto no es lo más eficiente, ya que aún tienes ciclos de todos los elementos, pero pensé que valdría la pena considerar el muy simple:
let keepGoing = true;
things.forEach( (thing) => {
if (noMore) keepGoing = false;
if (keepGoing) {
// do things with thing
}
});
La respuesta correcta depende de por qué quieres hacer esto.
Si lo que desea es: "una forma tan limpia y concisa de salir de forEach()
como sea posible, dado que no se permite la break
" , puede redefinir Array.forEach () para permitirle hacer esto:
[1,2,3,4,5].forEach((x,i,stop) => { // x and i are the standard 2 args
if (x > 3) {
stop() // You could call it ''end'', ''brk'' or whatever...
}
console.log(x)
})
Aquí está la anulación, tenga en cuenta que forEach()
normalmente pasa dos parámetros: el objeto iterado actualmente y el índice. Solo estamos agregando un tercero:
Array.prototype.forEach = function(fn) {
var StopIteration = new Error("StopIteration");
var len = this.length;
function stop() {
throw StopIteration;
}
for (i=0;i<len;i++) {
try {
fn(this[i], i, stop)
}
catch(e) {
if(e == StopIteration) {
return
}
throw e;
}
}
}
Puede usar el nombre que desee (excepto break
, o cualquier otra palabra clave reservada, ¡no hay suerte!)
(En un proyecto real, debería ir con una de las respuestas súper elevadas. Solo estoy agregando esto porque algunas personas pueden encontrar el concepto útil en otras aplicaciones).
Lo sé no de la manera correcta. No es romper el bucle. Es un jugad
let result = true;
[1, 2, 3].forEach(function(el) {
if(result){
console.log(el);
if (el === 2){
result = false;
}
}
});
No hay una habilidad incorporada para break
por cada forEach
. Para interrumpir la ejecución, tendría que 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 bucle for
tradicional podría ser más apropiado si realmente necesitas break
dentro de él.
Use Array#some
En su lugar, use Array#some
:
[1, 2, 3].some(function(el) {
console.log(el);
return el === 2;
});
Esto funciona porque some
devuelven true
tan pronto como cualquiera de las devoluciones de llamada, ejecutadas en orden de matriz, devuelven true
, cortocircuitando la ejecución del resto.
some
, son every
inversos (que se detendrán en una return false
), y para cada forEach
son todos los métodos de ECMAScript Fifth Edition que se deben agregar al Array.prototype
en los navegadores donde faltan.
Otro concepto que se me ocurrió:
function forEach(array, cb) {
var breakOnNext = false;
function _break() { breakOnNext = true; }
for (var i = 0, bound = array.length; i < bound; ++i) {
if (breakOnNext) { break; }
cb(array[i], i, array, _break);
}
}
Uso:
forEach([''a'',''b'',''c'',''d''], function (e, i, array, _break) {
console.log(e, i);
if (e === ''b'') { _break(); }
});
Es posible que necesite algunos ajustes, especialmente para admitir la iteración de la propiedad del objeto.
Otro enfoque mas
var wageType = types.filter(function(element){
if(e.params.data.text == element.name){
return element;
}
});
console.dir(wageType);
Prefiero usar for in
var words = [''a'', ''b'', ''c''];
var text = '''';
for (x in words) {
if (words[x] == ''b'') continue;
text += words[x];
}
console.log(text);
for in
funciona de manera muy similar a forEach
, y puede agregar return para salir dentro de la función. Mejor rendimiento también.
Puedes seguir el siguiente código que me funciona:
var loopStop = false;
YOUR_ARRAY.forEach(function loop(){
if(loopStop){ return; }
if(condition){ loopStop = true; }
});
Puedes usar every métodos:
[1,2,3].every(function(el) {
return !(el === 1);
});
para usar el viejo 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 here .
Respuesta corta: use for...break
para esto o cambie su código para evitar la ruptura de forEach
. No use .some()
o .every()
para emular for...break
. Vuelva a escribir su código para evitar for...break
bucle, o usar for...break
. Cada vez que usas estos métodos for...break
alternativa Dios mata al gatito.
Respuesta larga:
.some()
y .every()
devuelven ambos valores boolean
, .some()
devuelve true
si hay algún elemento para el que la función pasada devuelve true
, todo devuelve false
si hay algún elemento para el que la función pasada devuelve false
. Esto es lo que significan las funciones. Usar funciones para lo que no significan es mucho peor que usar tablas para maquetar en lugar de CSS, porque frustra a todos los que leen tu código.
Además, la única forma posible de utilizar estos métodos como alternativa a la for...break
es crear efectos secundarios (cambiar algunas variables fuera de la función de devolución de llamada .some()
), y esto no es muy diferente de la for...break
.
Por lo tanto, usar .some()
o .every()
for...break
alternativa de bucle de for...break
no está exenta de efectos secundarios, esto no es mucho más limpio que la for...break
, esto es frustrante, por lo que no es mejor.
Siempre puede volver a escribir su código para que no haya necesidad for...break
. Puede filtrar la matriz usando .filter()
, puede dividir la matriz usando .slice()
y así sucesivamente, y luego usar .forEach()
o .map()
para esa parte de la matriz.
Si desea mantener su sintaxis de forEach
, esta es una manera de mantenerla eficiente (aunque no tan buena como un bucle regular). Verifique de inmediato si hay una variable que sepa si quiere salir del ciclo.
Este ejemplo utiliza una función anónima para crear un ámbito de función alrededor del forEach
que necesita para almacenar la información realizada .
(function(){
var element = document.getElementById(''printed-result'');
var done = false;
[1,2,3,4].forEach(function(item){
if(done){ return; }
var text = document.createTextNode(item);
element.appendChild(text);
if (item === 2){
done = true;
return;
}
});
})();
<div id="printed-result"></div>
Mis dos centavos.
Si desea utilizar la sugerencia de Dean Edward y lanzar el error StopIteration para salir del ciclo sin tener que detectar el error, puede usar la siguiente función ( originalmente desde 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 a recordar es que esto solo actualizará la función Array.prototype.forEach si ya existe. Si no existe ya, no lo modificará.
Si necesita una ruptura basada en el valor de los elementos que ya están en su matriz como en su caso (es decir, si la condición de ruptura no depende de la variable de tiempo de ejecución que puede cambiar después de que la matriz tenga asignados sus valores de elementos), también puede usar la combinación de slice() y indexOf() siguiente manera.
Si necesitas romper cuando forEach llega a ''Apple'' puedes usar
var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var fruitsToLoop = fruits.slice(0, fruits.indexOf("Apple"));
// fruitsToLoop = Banana,Orange,Lemon
fruitsToLoop.forEach(function(el) {
// no need to break
});
Como se indica slice() el método slice () devuelve los elementos seleccionados en una matriz, como un nuevo objeto de matriz. La matriz original no se cambiará.
Espero que ayude a alguien.
Si no necesita acceder a su matriz después de la iteración, puede rescatarla estableciendo la longitud de la matriz en 0. Si todavía la necesita después de su iteración, puede clonarla utilizando la división.
[1,3,4,5,6,7,8,244,3,5,2].forEach(function (item, index, arr) {
if (index === 3) arr.length = 0;
});
O con un clon:
var x = [1,3,4,5,6,7,8,244,3,5,2];
x.slice().forEach(function (item, index, arr) {
if (index === 3) arr.length = 0;
});
Que es una solución mucho mejor que lanzar errores aleatorios en su código.
Utilice la función array.prototype.every
, que le proporciona la utilidad para interrumpir el bucle. Ver ejemplo aquí documentación de Javascript en la red de desarrolladores de Mozilla
Yo uso nullhack para ese propósito, intenta acceder a la propiedad de null
, que es un error:
try {
[1,2,3,4,5]
.forEach(
function ( val, idx, arr ) {
if ( val == 3 ) null.NULLBREAK;
}
);
} catch (e) {
// e <=> TypeError: null has no properties
}
//
prueba con "encontrar":
var myCategories = [
{category: "start", name: "Start", color: "#AC193D"},
{category: "action", name: "Action", color: "#8C0095"},
{category: "exit", name: "Exit", color: "#008A00"}
];
function findCategory(category) {
return myCategories.find(function(element) {
return element.category === category;
});
}
console.log(findCategory("start"));
// output: { category: "start", name: "Start", color: "#AC193D" }
var Book = {"Titles":[
{
"Book3" : "BULLETIN 3"
}
,
{
"Book1" : "BULLETIN 1"
}
,
{
"Book2" : "BULLETIN 2"
}
]}
var findbystr = function(str) {
var return_val;
Book.Titles.forEach(function(data){
if(typeof data[str] != ''undefined'')
{
return_val = data[str];
}
}, str)
return return_val;
}
book = findbystr(''Book1'');
console.log(book);