"Continuer" dans cursor.forEach ()

280

Je crée une application en utilisant meteor.js et MongoDB et j'ai une question à propos de cursor.forEach (). Je veux vérifier certaines conditions au début de chaque itération forEach, puis ignorer l'élément si je n'ai pas à effectuer l'opération dessus afin de gagner du temps.

Voici mon code:

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

Je sais que je pourrais transformer le curseur en tableau à l'aide de curs.find (). ).

Drag0
la source

Réponses:

562

Chaque itération de la forEach()va appeler la fonction que vous avez fournie. Pour arrêter le traitement ultérieur dans une itération donnée (et continuer avec l'élément suivant), il vous suffit de returnpartir de la fonction au point approprié:

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

  // This part will be avoided if not neccessary
  doSomeLengthyOperation();
});
nnnnnn
la source
18
Savez-vous peut-être ce que pourrait être la "pause" alors si continuer est juste "revenir";
Drag0
5
Je n'utilise pas MongoDB donc je n'ai pas lu sa documentation, mais il est possible que return false;ce soit l'équivalent de break;(comme c'est le cas pour une .each()boucle jQuery ). Bien sûr, celui qui a implémenté MongoDB .forEach()peut avoir eu d'autres idées ...
nnnnnn
10
@ Drag0 Vous pouvez utiliser .some () en remplacement de .forEach (), qui vous permet de retourner false pour rompre la boucle.
Andrew
6
@Andrew Vous pouvez utiliser some, sachez simplement que vous utilisez à mauvais escient (ou utilisez de manière créative) une fonction qui était destinée à déterminer si l'un des éléments correspond à la condition. Un peu comme quand je vois des gens utiliser mapet ignorer le résultat (ils auraient dû utiliser forEach). C'est de la sémantique, les gens devront regarder deux fois pour savoir pourquoi vous utilisez somequand vous ne vous souciez pas vraiment du résultat
Juan Mendes
1
@Andrew excellent conseil, mais c'est return truece qui rompra la boucle
daviestar
11

À mon avis, la meilleure approche pour y parvenir en utilisant la filter méthode car cela n'a aucun sens de revenir dans un forEachbloc; pour un exemple sur votre extrait:

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

Cela affinera votre elementsCollectionet ne conservera que les filtredéléments à traiter.

Ramy Tamer
la source
3
Cela itérerait les éléments trouvés deux fois, une fois dans la filterseconde et la seconde dans le forEachcas s'il s'agit d'une grande collection, elle sera très inefficace
Dementic
1
Vous avez raison, mais je ne pense pas que ce soit un gros problème, car la complexité temporelle de cette situation pourrait O(2n)être considérée comme O(n).
Ramy Tamer
2
Considérant que le SO est utilisé par d'autres, pas seulement par le PO, publier une solution juste pour la publier, crée plus de mal que de bien. La réponse ci-dessus le fait en une seule itération et est la rightfaçon de le faire.
Dementic
Notez que la collection de l'OP n'est pas un tableau, c'est un objet curseur Mongo DB, qui ne semble pas avoir de .filter()méthode, vous devez donc appeler sa .toArray()méthode avant de pouvoir.filter()
nnnnnn
7

Voici une solution utilisant for ofet continueau lieu 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();

});

Cela peut être un peu plus utile si vous devez utiliser des fonctions asynchrones dans votre boucle qui ne fonctionnent pas à l'intérieur forEach. Par exemple:


(async fuction(){

for (let el of elementsCollection) {

    if (!el.shouldBeProcessed){
        continue;
    }

    let res;

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

});

})()
jwerre
la source
2

Utilisation de l' évaluation des courts-circuits JavaScripts . Si el.shouldBeProcessedrenvoie vrai,doSomeLengthyOperation

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