cómo romper la función _.each en underscore.js

200

Estoy buscando una manera de detener las iteraciones de underscore.js _.each() método , pero no puedo encontrar la solución. jQuery .each()puede romperse si lo haces return false.

¿Hay alguna manera de dejar de subrayar cada ()?

_([1,2,3]).each(function(v){
    if (v==2) return /*what?*/;
})
dy_
fuente
44
No creo que sea posible, porque la forEachfunción nativa tampoco ofrece esta característica.
Felix Kling
8
Normalmente, cuando se usa eachcon un cierre (en la mayoría de los idiomas), primero desea filtrar su lista. De esa manera no tienes que preocuparte por romper con eso. En términos generales, si necesita salir temprano de una iteración, probablemente haya una forma diferente de hacerlo.
Rob Hruska
Aquí hay un par de preguntas relacionadas con Groovy, donde el comportamiento (incapacidad para romper convenientemente un eachcierre) es similar a JavaScript.
Rob Hruska
@Dmitry_F, como otros han notado, no puedes hacer exactamente lo que estás pidiendo. Pero como lo demostré, puede usar Array.everypara emular el comportamiento que desea.
aeskr
@Robar. Salud. Primer comentario realmente útil. De hecho, podría haberlo hecho de otra manera.
net.uk.sweet

Respuestas:

267

No puede romper con el eachmétodo: emula el forEachcomportamiento del método nativo y el nativo forEachno proporciona escapar del ciclo (aparte de lanzar una excepción).

¡Sin embargo, no todo está perdido! Puedes usar elArray.every método. :)

Desde ese enlace:

everyejecuta la callbackfunción proporcionada una vez para cada elemento presente en la matriz hasta que encuentre uno donde callbackdevuelva un valor falso. Si se encuentra dicho elemento, el everymétodo devuelve inmediatamente falso.

En otras palabras, podría hacer algo complicado como este ( enlace a JSFiddle ):

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

Esto alertará 1a través 3, y luego "romper" fuera de onda.

Está utilizando underscore.js, por lo que le complacerá saber que proporciona un everymétodo, lo llaman every, pero como ese enlace menciona, también proporcionan un alias llamado all.

aeskr
fuente
2
¿Underscore.js proporciona una implementación para esto también?
Felix Kling
1
@FelixKling, sí. He añadido eso a mi respuesta.
aeskr
2
en este momento (05/2013), no hay _.every()un _.all()método ni un método para las matrices en guión bajo, así que manténgase en el Array.every().
pkyeck
3
Esto funcionará, pero es una razón habitual para usar every. Así que ten cuidado con la legibilidad.
evanrmurphy
3
El subrayado de documentos para _.each()tiene una nota específica sobre el hecho de que no puede salir del ciclo, y recomienda que lo use _.find()en su lugar. http://underscorejs.org/#each
blatt
70

Actualizar:

_.find sería mejor ya que sale del bucle cuando se encuentra el elemento:

var searchArr = [{id:1,text:"foo"},{id:2,text:"bar"}];
var count = 0;
var filteredEl = _.find(searchArr,function(arrEl){ 
              count = count +1;
              if(arrEl.id === 1 ){
                  return arrEl;
              }
            });

console.log(filteredEl);
//since we are searching the first element in the array, the count will be one
console.log(count);
//output: filteredEl : {id:1,text:"foo"} , count: 1

** Antiguo **

Si desea salir de un bucle condicionalmente, use _.filter api en lugar de _.each. Aquí hay un fragmento de código

var searchArr = [{id:1,text:"foo"},{id:2,text:"bar"}];
var filteredEl = _.filter(searchArr,function(arrEl){ 
                  if(arrEl.id === 1 ){
                      return arrEl;
                  }
                });
console.log(filteredEl);
//output: {id:1,text:"foo"}
Nikhil
fuente
1
esto no rompe el ciclo, solo filtra la matriz. imagina que no tienes 2 sino 20,000 artículos en la matriz. su registro solo generará el ejemplo que publicó, pero el ciclo se ejecutará 20,000 veces :(
pkyeck
@pkyeck tiene razón, puede ser _.find es mejor que _.filter ya que se rompe después de que se encuentra el elemento, aquí está el violín: jsfiddle.net/niki4810/9K3EV
Nikhil
2
Creo que esta respuesta debería estar marcada como la correcta. _.findhace exactamente lo que se le pide: iterar sobre la lista hasta que regrese la devolución de llamada true.
Fabien Quatravaux
Voté por esta respuesta porque la respuesta aceptada (Array.every) no funcionará en objetos, pero _.find () sí.
mate
Y eso es lo que se recomienda en los documentos: también es bueno tener en cuenta que no se puede romper un bucle de cada uno: para romper, use _.find en su lugar.
shaharsol
15

Puedes echar un vistazo a en _.somelugar de _.each. _.somedeja de recorrer la lista una vez que un predicado es verdadero. Los resultados se pueden almacenar en una variable externa.

_.some([1, 2, 3], function(v) {
    if (v == 2) return true;
})

Ver http://underscorejs.org/#some

Joan
fuente
6
_([1,2,3]).find(function(v){
    return v if (v==2);
})
Rockyboy_ruby
fuente
3

Tal vez desee el subrayado any () o find (), que detendrá el procesamiento cuando se cumpla una condición.

Grantwparks
fuente
3

No puede romper un forEachguión bajo, ya que emula el comportamiento nativo de EcmaScript 5.

JaredMcAteer
fuente
2

Creo que si su matriz fuera realmente un objeto, podría regresar usando un objeto vacío.

_.({1,2,3,4,5}).each(function(v){  
  if(v===3) return {}; 
});
bm_i
fuente
Eso solo sucede con EcmaScript v <5, ya que la comparación que hace el subrayado para verificar si está devolviendo el objeto vacío en la alternativa provista a forEach solo se realiza cuando el nativo no está disponible.
Alfonso de la Osa
1

Actualizar:

En realidad, puede "romper" arrojando un error adentro y atrapándolo afuera: algo como esto:

try{
  _([1,2,3]).each(function(v){
    if (v==2) throw new Error('break');
  });
}catch(e){
  if(e.message === 'break'){
    //break successful
  }
}

Obviamente, esto tiene algunas implicaciones con respecto a cualquier otra excepción que su código active en el ciclo, ¡así que use con precaución!

bm_i
fuente
Me encanta cómo obtengo tantos votos
negativos
1
Llego tarde a la fiesta, pero tenga en cuenta que ese tipo no solo dijo lo que sugirió, sino que ofreció dos alternativas más (y más adecuadas). El único ofreció el "hack" en caso de que el usuario lo quiera por todos los medios. En cambio, solo ofreciste el feo truco.
Areks
0

trabajado en mi caso

var arr2 = _.filter(arr, function(item){
    if ( item == 3 ) return item;
});
aleXela
fuente