«Продолжить» в cursor.forEach ()

280

Я создаю приложение, используя meteor.js и MongoDB, и у меня есть вопрос о cursor.forEach (). Я хочу проверить некоторые условия в начале каждой итерации forEach, а затем пропустить элемент, если мне не нужно выполнять над ним операцию, чтобы сэкономить время.

Вот мой код:

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

Я знаю, что могу превратить курсор в массив, используя cursor.find (). Fetch (), а затем использовать обычный цикл for для перебора элементов и использовать continue и break нормально, но мне интересно, есть ли что-то похожее для использования в forEach ( ).

Drag0
источник

Ответы:

562

Каждая итерация forEach()будет вызывать функцию, которую вы предоставили. Чтобы остановить дальнейшую обработку в любой данной итерации (и перейти к следующему пункту), вам просто нужно выйти returnиз функции в соответствующую точку:

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

  // This part will be avoided if not neccessary
  doSomeLengthyOperation();
});
NNNNNN
источник
18
Знаете ли вы, может быть, что может быть "перерывом", тогда если продолжить это просто "возврат";
Drag0
5
Я не использую MongoDB, поэтому не читал его документацию, но, возможно, это return false;было бы эквивалентно break;(как для .each()цикла jQuery ). Конечно, кто бы ни реализовывал MongoDB, .forEach()у него могли быть другие идеи ...
nnnnnn
10
@ Drag0 Вы можете использовать .some () в качестве замены .forEach (), что позволяет вам возвращать false, чтобы разорвать цикл.
Андрей
6
@Andrew Вы можете использовать some, просто имейте в виду, что вы неправильно используете (или творчески используете) функцию, которая должна была сказать, соответствует ли какой-либо элемент условию. В некотором роде, когда я вижу, как люди используют mapи игнорируют результат (они должны были использовать forEach). Это семантика, люди должны будут дважды взглянуть, чтобы понять, почему вы используете, someкогда вы на самом деле не заботитесь о результате
Хуан Мендес,
1
@ Отличный совет для return true
Андрея
11

На мой взгляд, лучший подход для достижения этого - использовать filter метод, так как возвращать в forEachблок бессмысленно ; для примера в вашем фрагменте:

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

Это сузит ваш кругозор elementsCollectionи просто сохранит filtredэлементы, которые должны быть обработаны.

Рами Тамер
источник
3
Это будет повторять найденные элементы дважды, один раз в filterи второй в forEachслучае, если это большая коллекция, это будет очень неэффективно
Dementic
1
Вы правы, но я не думаю, что это имеет большое значение, поскольку временная сложность этого O(2n)может рассматриваться как O(n).
Рами Тамер
2
Учитывая, что SO используется другими, а не только OP, размещение решения только с целью опубликования создает больше вреда, чем пользы. Ответ выше делает это за одну итерацию и является rightспособом сделать это.
Дементик
Обратите внимание, что коллекция OP не является массивом, это объект-курсор Mongo DB, который, похоже, не имеет .filter()метода, поэтому вам придется вызывать его .toArray()метод, прежде чем вы сможете это сделать.filter()
nnnnnn
7

Вот решение с использованием for ofи continueвместо 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();

});

Это может быть немного более полезно, если вам нужно использовать асинхронные функции внутри цикла, которые не работают внутри forEach. Например:


(async fuction(){

for (let el of elementsCollection) {

    if (!el.shouldBeProcessed){
        continue;
    }

    let res;

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

});

})()
jwerre
источник
2

Использование оценки короткого замыкания JavaScripts . Если el.shouldBeProcessedвозвращает true,doSomeLengthyOperation

elementsCollection.forEach( el => 
  el.shouldBeProcessed && doSomeLengthyOperation()
);
JSON C11
источник