"Continua" in cursor.forEach ()


280

Sto creando un'app usando meteor.js e MongoDB e ho una domanda su cursor.forEach (). Voglio controllare alcune condizioni all'inizio di ciascuna per ogni iterazione e quindi saltare l'elemento se non devo eseguire l'operazione su di esso in modo da poter risparmiare un po 'di tempo.

Ecco il mio codice:

// 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();
  }
});

So che potrei girare il cursore in array usando cursor.find (). Fetch () e quindi usare il regolare for-loop per scorrere gli elementi e usare continue e break normalmente, ma sono interessato se c'è qualcosa di simile da usare in forEach ( ).

Risposte:


562

Ogni iterazione di forEach()chiamerà la funzione che hai fornito. Per interrompere l'ulteriore elaborazione all'interno di una data iterazione (e continuare con l'elemento successivo) devi solo returndalla funzione nel punto appropriato:

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

  // This part will be avoided if not neccessary
  doSomeLengthyOperation();
});

18
Sai forse quale potrebbe essere la "pausa", quindi se continui è solo "ritorno";
Trascina0,

5
Non uso MongoDB quindi non ho letto la sua documentazione, ma è possibile che return false;sarebbe l'equivalente di break;(come è per un .each()ciclo jQuery ). Naturalmente chiunque .forEach()avesse implementato MongoDB avrebbe potuto avere altre idee ...
nnnnnn,

10
@ Drag0 È possibile utilizzare .some () in sostituzione di .forEach (), che consente di restituire false per interrompere il ciclo.
Andrew,

6
@Andrew È possibile utilizzare some, basta essere consapevoli del fatto che si sta abusando (o utilizzando in modo creativo) una funzione che era destinata a dire se uno qualsiasi degli elementi corrisponde alla condizione. Un po 'come quando vedo le persone usare mape ignorare il risultato (avrebbero dovuto usare forEach). È semantica, le persone dovranno guardare due volte per sapere perché stai usando somequando non ti interessa davvero il risultato
Juan Mendes,

1
@Andrew ottimo consiglio, tuttavia è return trueche interromperà il ciclo
daviestar il

11

A mio avviso, l'approccio migliore per raggiungere questo obiettivo è utilizzare il filter metodo poiché non ha senso tornare in forEachblocco; per un esempio sul tuo frammento:

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

Questo restringerà il tuo elementsCollectione manterrà solo gli filtredelementi che dovrebbero essere elaborati.


3
Ciò ripeterebbe due volte gli elementi trovati, una volta nel filtere il secondo nel forEachcaso in cui fosse una grande raccolta, sarebbe molto inefficiente
Demenza

1
Hai ragione, ma non penso che sia un grosso problema in quanto la complessità temporale di questo sarebbe O(2n)che può essere considerata come O(n).
Ramy Tamer,

2
Considerare che la SO viene utilizzata da altri, non solo l'OP, pubblicare una soluzione solo allo scopo di pubblicarla, sta creando più danni che benefici. La risposta sopra lo fa in una iterazione ed è il rightmodo di farlo.
Demenza

Nota che la raccolta dell'OP non è un array, è un oggetto cursore Mongo DB, che non sembra avere un .filter()metodo, quindi dovresti chiamarlo .toArray()prima di poterlo fare.filter()
nnnnnn

7

Ecco una soluzione che utilizza for ofe continueinvece di 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();

});

Questo potrebbe essere un po 'più utile se hai bisogno di usare funzioni asincrone all'interno del tuo loop che non funzionano all'interno forEach. Per esempio:


(async fuction(){

for (let el of elementsCollection) {

    if (!el.shouldBeProcessed){
        continue;
    }

    let res;

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

});

})()

2

Facendo uso della valutazione del cortocircuito di JavaScripts . Se el.shouldBeProcessedrestituisce true,doSomeLengthyOperation

elementsCollection.forEach( el => 
  el.shouldBeProcessed && doSomeLengthyOperation()
);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.