jak przerwać funkcję _.each w underscore.js

200

Szukam sposobu na zatrzymanie iteracji _.each()metody underscore.js , ale nie mogę znaleźć rozwiązania. jQuery .each()może się zepsuć, jeśli to zrobisz return false.

Czy istnieje sposób, aby zatrzymać podkreślenie każdego ()?

_([1,2,3]).each(function(v){
    if (v==2) return /*what?*/;
})
dy_
źródło
4
Nie sądzę, aby było to możliwe, ponieważ forEachfunkcja natywna również nie oferuje tej funkcji.
Felix Kling
8
Zwykle, gdy używasz eachzamknięcia (w większości języków), najpierw musisz przefiltrować listę. W ten sposób nie musisz się martwić o zerwanie z tym. Ogólnie rzecz biorąc, jeśli musisz wcześnie przerwać iterację, prawdopodobnie jest inny sposób, aby to zrobić.
Rob Hruska
Oto kilka powiązanych pytań do Groovy, w których zachowanie (niemożność wygodnego zerwania z eachzamknięcia) jest podobne do JavaScript.
Rob Hruska
@Dmitry_F, jak zauważyli inni, nie możesz zrobić dokładnie tego, o co prosisz. Ale jak wykazałem, możesz użyć Array.everydo naśladowania pożądanego zachowania.
aeskr
@Obrabować. Twoje zdrowie. Pierwszy komentarz bardzo pomocny. Rzeczywiście był inny sposób, w jaki mogłem to zrobić.
net.uk.sweet

Odpowiedzi:

267

Nie można zerwać z eachmetodą - emuluje ona forEachzachowanie metody natywnej, a metoda natywna forEachnie zapewnia możliwości ucieczki z pętli (poza zgłaszaniem wyjątku).

Jednak cała nadzieja nie jest stracona! Możesz użyć tej Array.everymetody. :)

Z tego linku:

everywykonuje podaną callbackfunkcję jeden raz dla każdego elementu obecnego w tablicy, aż znajdzie taki, w którym callbackzwraca wartość false. Jeśli taki element zostanie znaleziony, everymetoda natychmiast zwraca false.

Innymi słowy, możesz zrobić coś tak skomplikowanego jak ten ( link do JSFiddle ):

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

Będzie to ostrzegać 1przez 3, a następnie „break” poza pętli.

Używasz underscore.js, więc będziesz zadowolony, aby dowiedzieć się, że nie zapewniają everymetody-nazywają go every, ale jak linkujące wspomina, zapewniają również alias o nazwie all.

aeskr
źródło
2
Czy underscore.js zapewnia również implementację tego?
Felix Kling
1
@FelixKling, tak to robi. Dodałem to do mojej odpowiedzi.
aeskr
2
w tej chwili (05/2013) w podkreśleniu _.every()nie ma _.all()metody ani tablic, więc trzymaj się Array.every().
pkyeck
3
To zadziała, ale jest to zwykły powód używania every. Uważaj więc na czytelność.
evanrmurphy
3
Dokumenty podkreślenia dla _.each()mają notatkę konkretnie o tym, że nie można wyjść z pętli, i zalecamy użycie _.find()zamiast tego. http://underscorejs.org/#each
blatt
70

Aktualizacja:

_.find byłby lepszy, ponieważ wyłamuje się z pętli po znalezieniu elementu:

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

** stary **

Jeśli chcesz warunkowo wyjść z pętli, użyj _.filter api zamiast _.each. Oto fragment kodu

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"}
Nikhil
źródło
1
to nie przerywa pętli - po prostu filtruje tablicę. wyobraź sobie, że nie masz 2, ale 20 000 pozycji w tablicy. dziennik wyświetliłby tylko przykład, który opublikowałeś, ale pętla uruchomiłaby się 20 000 razy :(
pkyeck
@pkyeck masz rację, może być _.find jest lepsza niż _.filter gdy pęknie po elemnt zostanie znaleziony, tutaj jest na skrzypcach: jsfiddle.net/niki4810/9K3EV
Nikhil
2
Myślę, że ta odpowiedź powinna być oznaczona jako poprawna. _.findwykonuje dokładnie to, co jest wymagane: iteruje listę, aż do powrotu true.
Fabien Quatravaux,
Zagłosowano na tę odpowiedź, ponieważ zaakceptowana odpowiedź (Array.every) nie będzie działać na obiektach, ale _.find () zadziała.
mat
I to jest zalecane w dokumentach: Warto również zauważyć, że każdej pętli nie można przerwać - aby przerwać, użyj _.find zamiast tego.
shaharsol
15

Możesz zajrzeć _.somezamiast _.each. _.someprzestaje przeglądać listę, gdy predykat jest prawdziwy. Wyniki można zapisać w zmiennej zewnętrznej.

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

Zobacz http://underscorejs.org/#some

Joan
źródło
6
_([1,2,3]).find(function(v){
    return v if (v==2);
})
Rockyboy_ruby
źródło
3

Być może potrzebujesz znaku podkreślenia any () lub find (), który przestanie przetwarzać, gdy zostanie spełniony warunek.

grantwparki
źródło
3

Podobnie jak inne odpowiedzi, jest to niemożliwe. Oto komentarz na temat przerywnika w numerze podkreślenia nr 21

czizzy
źródło
3

Nie można złamać forEachznaku podkreślenia, ponieważ emuluje on natywne zachowanie EcmaScript 5.

JaredMcAteer
źródło
2

Wierzę, że jeśli twoja tablica była w rzeczywistości obiektem, możesz zwrócić za pomocą pustego obiektu.

_.({1,2,3,4,5}).each(function(v){  
  if(v===3) return {}; 
});
bm_i
źródło
Dzieje się tak tylko w przypadku EcmaScript v <5, ponieważ porównanie podkreśla, aby sprawdzić, czy zwracany jest pusty obiekt w podanej alternatywie dla forEach, tylko wtedy, gdy natywny nie jest dostępny.
Alfonso de la Osa
1

Aktualizacja:

Możesz faktycznie „złamać”, wrzucając błąd do środka i łapiąc go na zewnątrz: coś takiego:

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

Ma to oczywiście pewne implikacje dotyczące wszelkich innych wyjątków, które kod wyzwala w pętli, więc używaj go ostrożnie!

bm_i
źródło
Uwielbiam to, jak
zdobywam
1
Jestem spóźniony na imprezę, ale zauważ, że facet nie tylko powiedział to, co zasugerowałeś, ale zaoferował jeszcze dwie alternatywy (i bardziej odpowiednie). Jedyny oferowany „hack” na wypadek, gdyby użytkownik z całą pewnością tego chciał. Zamiast tego zaoferowałeś tylko brzydki hack.
Areks
0

pracował w moim przypadku

var arr2 = _.filter(arr, function(item){
    if ( item == 3 ) return item;
});
aleXela
źródło