як зламати функцію _.each в underscore.js


200

Я шукаю спосіб зупинити ітерації _.each()методу underscore.js , але не можу знайти рішення. jQuery .each()може зламатися, якщо ви це зробите return false.

Чи є спосіб зупинити підкреслення кожного ()?

_([1,2,3]).each(function(v){
    if (v==2) return /*what?*/;
})

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.


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, оскільки він зламається після виявлення елементу, ось скрипка: jsfiddle.net/niki4810/9K3EV
Nikhil

2
Я думаю, що цю відповідь слід позначити як правильну. _.findробить саме те, що запитується: повторюйте список, поки не повернеться зворотній дзвінок true.
Fabien Quatravaux

Проголосував за цю відповідь, оскільки прийнята відповідь (Array.every) не буде працювати на об'єктах, але _.find () буде.
мат

І ось що рекомендується в документах: Добре також зазначити, що кожен цикл не може бути відірваний - щоб зламати, використовуйте замість _.find.
шахарсол

15

Можна _.someзамість цього поглянути _.each. _.someприпиняє обхід списку, коли предикат є істинним. Результат (и) можуть бути збережені у зовнішній змінній.

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

Дивіться http://underscorejs.org/#some



3

Можливо, ви хочете підкреслити будь-який () або знайти (), який припинить обробку, коли буде виконано умову.



3

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


2

Я вважаю, що якщо ваш масив був насправді об'єктом, ви могли б повернутися за допомогою порожнього об'єкта.

_.({1,2,3,4,5}).each(function(v){  
  if(v===3) return {}; 
});

Це відбувається лише з EcmaScript v <5 як порівняння, яке підкреслення робить, щоб перевірити, чи повертаєте ви порожній об'єкт у наданій альтернативі forEach, робиться лише тоді, коли рідного немає в наявності.
Альфонсо де ла Оса


1

Оновлення:

Ви насправді можете "зламати", кинувши помилку всередину та виявивши її зовні: щось подібне:

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

Це, очевидно, має певні наслідки щодо будь-яких інших винятків, які ваш код запускає в циклі, тому використовуйте з обережністю!


Любов , як я отримую так багато вниз голосів за це, і цей хлопець отримує весь вантаж злетів за його stackoverflow.com/a/2641374/674720
bm_i

1
Я спізнююсь на вечірку, але зауважте, що хлопець не тільки сказав те, що ви запропонували, але запропонував ще дві альтернативи (і більш підходящі). Єдиний запропонований "хак", якщо користувач усіма силами цього хоче. Ви натомість запропонували лише некрасивий злом.
Арекс

0

працював у моєму випадку

var arr2 = _.filter(arr, function(item){
    if ( item == 3 ) return item;
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.