как сломать функцию _.each в underscore.js

200

Я ищу способ остановить итерации _.each()метода underscore.js , но не могу найти решение. JQuery .each()может сломаться, если вы это сделаете return false.

Есть ли способ прекратить подчеркивать каждый ()?

_([1,2,3]).each(function(v){
    if (v==2) return /*what?*/;
})
dy_
источник
4
Я не думаю, что это возможно, потому что нативная forEachфункция также не предлагает эту функцию.
Феликс Клинг
8
Обычно при использовании eachс замыканием (на большинстве языков) сначала необходимо отфильтровать список. Таким образом, вам не нужно беспокоиться о том, чтобы оторваться от него. Вообще говоря, если вам нужно рано выйти из итерации, возможно, есть другой способ сделать это.
Роб Хруска
Вот пара связанных вопросов для Groovy, где поведение (неспособность удобно отделиться от eachзамыкания) похоже на JavaScript.
Роб Хруска
@Dmitry_F, как уже отмечали другие, вы не можете делать именно то, что вы просите. Но, как я продемонстрировал, вы можете использовать, Array.everyчтобы имитировать поведение, которое вы хотите.
aeskr
@Rob. Приветствия. Первый комментарий действительно полезен. На самом деле, я мог сделать это по-другому.
net.uk.sweet

Ответы:

267

Вы не можете оторваться от eachметода - он эмулирует forEachповедение нативного метода, а нативный forEachне обеспечивает выхода из цикла (кроме создания исключения).

Однако все надежды не потеряны! Вы можете использовать Array.everyметод. :)

По этой ссылке:

everyвыполняет предоставленную callbackфункцию один раз для каждого элемента, присутствующего в массиве, пока не найдет тот, где callbackвозвращается ложное значение. Если такой элемент найден, everyметод немедленно возвращает false.

Другими словами, вы можете сделать что-то запутанное, как это ( ссылка на JSFiddle ):

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

Это предупредит 1через 3, а затем «сломать» из цикла.

Вы используете underscore.js, так что вы будете рады узнать , что это действительно обеспечивает everyметод-они называют его every, но , как ссылка упоминает, они также предоставляют псевдоним с именем all.

aeskr
источник
2
Предоставляет ли underscore.js реализацию для этого?
Феликс Клинг
1
@FelixKling, да, это так. Я добавил это к своему ответу.
aeskr
2
сейчас (05/2013) нет ни метода, _.every()ни _.all()метода для массивов в подчеркивании - так что придерживайтесь Array.every().
pkyeck
3
Это будет работать, но это обычная причина для использования every. Так что следите за удобочитаемостью.
evanrmurphy
3
Подчеркнутые документы для _.each()имеют примечание конкретно о том факте, что вы не можете выйти из цикла, и рекомендуем использовать _.find()вместо этого. http://underscorejs.org/#each
blatt
70

Обновить:

_.find будет лучше, поскольку он выходит из цикла, когда элемент найден:

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

** Старый **

Если вы хотите условно выйти из цикла, используйте _.filter api вместо _.each. Вот фрагмент кода

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"}
Нихилу
источник
1
это не нарушает цикл - оно просто фильтрует массив. представьте, что у вас есть не 2, а 20 000 элементов в массиве. ваш лог будет выводить только тот пример, который вы опубликовали, но цикл будет выполняться 20.000 раз :(
pkyeck
@pkyeck вы правы, может быть _.find лучше _.filter , как он ломает после elemnt найдено, здесь скрипки: jsfiddle.net/niki4810/9K3EV
Нихилу
2
Я думаю, что этот ответ должен быть помечен как правильный. _.findделает именно то, что запрашивается: перебирать список до тех пор, пока обратный вызов не вернется true.
Фабьен Кватраво
Голосовали за этот ответ, потому что принятый ответ (Array.every) не будет работать с объектами, но _.find () будет работать.
Мэтт
И это то, что рекомендуется в документах: также хорошо отметить, что каждый цикл не может быть разорван - для разрыва используйте вместо него _.find.
Шахарсол
15

Вы можете посмотреть _.someвместо _.each. _.someпрекращает обход списка после того, как предикат верен. Результаты могут быть сохранены во внешней переменной.

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

Смотрите http://underscorejs.org/#some

Джоан
источник
6
_([1,2,3]).find(function(v){
    return v if (v==2);
})
Rockyboy_ruby
источник
3

Может быть, вы хотите Underscore any () или find (), который остановит обработку при выполнении условия.

grantwparks
источник
3

Вы не можете разбить forEachсимвол подчеркивания, так как он имитирует поведение EcmaScript 5.

JaredMcAteer
источник
2

Я считаю, что если бы ваш массив был на самом деле объектом, вы могли бы вернуться, используя пустой объект.

_.({1,2,3,4,5}).each(function(v){  
  if(v===3) return {}; 
});
bm_i
источник
Это происходит только с EcmaScript v <5, поскольку сравнение, которое подчеркивает, проверяет, возвращаете ли вы пустой объект в предоставленной альтернативе forEach, выполняется только тогда, когда собственный объект недоступен.
Альфонсо де ла Оса
2

Также хорошо отметить, что каждый цикл не может быть разорван - для разрыва используйте вместо него _.find.

http://underscorejs.org/#each

percebus
источник
1

Обновить:

Вы можете «сломаться», выбросив ошибку внутрь и поймав ее снаружи: что-то вроде этого:

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

Это, очевидно, имеет некоторые последствия в отношении любых других исключений, которые ваш код вызывает в цикле, поэтому используйте его с осторожностью!

bm_i
источник
Мне нравится, что за это я получил так много голосов "за", и этот парень получил целую
кучу
1
Я опаздываю на вечеринку, но учтите, что парень не только сказал то, что вы предложили, но и предложил еще две альтернативы (и более подходящую). Единственный предложенный «взлом» на случай, если пользователь непременно этого захочет. Вы вместо этого предложили только уродливый хак.
Арекс
0

работал в моем случае

var arr2 = _.filter(arr, function(item){
    if ( item == 3 ) return item;
});
ALEXELA
источник