"Continuar" en cursor.forEach ()

280

Estoy creando una aplicación usando meteor.js y MongoDB y tengo una pregunta sobre cursor.forEach (). Quiero verificar algunas condiciones al comienzo de cada iteración y luego omitir el elemento si no tengo que hacer la operación para poder ahorrar algo de tiempo.

Aquí está mi código:

// Fetch all objects in SomeElements collection
var elementsCollection = SomeElements.find();
elementsCollection.forEach(function(element){
  if (element.shouldBeProcessed == false){
    // Here I would like to continue to the next element if this one 
    // doesn't have to be processed
  }else{
    // This part should be avoided if not neccessary
    doSomeLengthyOperation();
  }
});

Sé que podría convertir el cursor en matriz usando cursor.find (). Fetch () y luego usar el ciclo for regular para iterar sobre los elementos y usar continue y break normalmente, pero estoy interesado si hay algo similar para usar en forEach ( )

Arrastrar0
fuente

Respuestas:

562

Cada iteración de la forEach()llamará a la función que ha proporcionado. Para detener el procesamiento adicional dentro de cualquier iteración dada (y continuar con el siguiente elemento) solo tiene que hacerlo returndesde la función en el punto apropiado:

elementsCollection.forEach(function(element){
  if (!element.shouldBeProcessed)
    return; // stop processing this iteration

  // This part will be avoided if not neccessary
  doSomeLengthyOperation();
});
nnnnnn
fuente
18
¿Sabes tal vez cuál podría ser el "descanso", entonces si continuar es solo "volver"?
Drag0
55
No uso MongoDB, así que no he leído su documentación, pero es posible que return false;sea ​​el equivalente de break;(como lo es para un .each()bucle jQuery ). Por supuesto, quien implementó MongoDB .forEach()puede haber tenido otras ideas ...
nnnnnn
10
@ Drag0 Puede usar .some () como reemplazo de .forEach (), que le permite devolver falso para romper el ciclo.
Andrew
66
@Andrew Puede usar some, solo tenga en cuenta que está haciendo un mal uso (o está usando de manera creativa) una función que estaba destinada a determinar si alguno de los elementos coincide con la condición. Algo así como cuando veo que la gente usa mape ignora el resultado (deberían haberlo usado forEach). Es semántica, la gente tendrá que mirar dos veces para saber por qué estás usando somecuando realmente no te importa el resultado
Juan Mendes
1
@Andrew gran consejo, sin embargo, es return truelo que romperá el círculo
daviestar
11

En mi opinión, el mejor enfoque para lograr esto es usar el filter método ya que no tiene sentido regresar en un forEachbloque; para un ejemplo en tu fragmento:

// Fetch all objects in SomeElements collection
var elementsCollection = SomeElements.find();
elementsCollection
.filter(function(element) {
  return element.shouldBeProcessed;
})
.forEach(function(element){
  doSomeLengthyOperation();
});

Esto reducirá su elementsCollectiony solo mantendrá los filtredelementos que deben procesarse.

Ramy Tamer
fuente
3
Esto iteraría los elementos encontrados dos veces, una en la filtersegunda y la segunda forEachsi es una colección grande, será muy ineficiente
Demencia el
1
Tienes razón, pero no creo que sea un gran problema ya que la complejidad del tiempo de esto sería lo O(2n)que se puede considerar O(n).
Ramy Tamer
2
Teniendo en cuenta que SO está siendo utilizado por otros, no solo el OP, publicar una solución solo con el propósito de publicarlo, está creando más daño que bien. La respuesta anterior lo hace en una iteración y es la rightforma de hacerlo.
Demencia el
Tenga en cuenta que la colección del OP no es una matriz, es un objeto de cursor Mongo DB, que no parece tener un .filter()método, por lo que debería llamar a su .toArray()método antes de poder .filter()
hacerlo
7

Aquí hay una solución usando for ofy en continuelugar de forEach:


let elementsCollection = SomeElements.find();

for (let el of elementsCollection) {

    // continue will exit out of the current 
    // iteration and continue on to the next
    if (!el.shouldBeProcessed){
        continue;
    }

    doSomeLengthyOperation();

});

Esto puede ser un poco más útil si necesita utilizar funciones asincrónicas dentro de su ciclo que no funcionan dentro forEach. Por ejemplo:


(async fuction(){

for (let el of elementsCollection) {

    if (!el.shouldBeProcessed){
        continue;
    }

    let res;

    try {
        res = await doSomeLengthyAsyncOperation();
    } catch (err) {
        return Promise.reject(err)
    }

});

})()
jwerre
fuente
2

Haciendo uso de la evaluación de cortocircuito JavaScripts . Si el.shouldBeProcesseddevuelve verdadero,doSomeLengthyOperation

elementsCollection.forEach( el => 
  el.shouldBeProcessed && doSomeLengthyOperation()
);
JSON C11
fuente