2008-10-08 10 views
274

Jaki jest najlepszy sposób na przerwanie pętli zagnieżdżonych w JavaScript?Najlepszy sposób na przerwanie pętli zagnieżdżonych w JavaScript?

//Write the links to the page. 
for (var x = 0; x < Args.length; x++) 
{ 
    for (var Heading in Navigation.Headings) 
    { 
     for (var Item in Navigation.Headings[Heading]) 
     { 
     if (Args[x] == Navigation.Headings[Heading][Item].Name) 
     { 
      document.write("<a href=\"" 
       + Navigation.Headings[Heading][Item].URL + "\">" 
       + Navigation.Headings[Heading][Item].Name + "</a> : "); 
      break; // <---HERE, I need to break out of two loops. 
     } 
     } 
    } 
} 
+0

Oto dobry przykład wyłamywania się z pętli i bloków kodu: https://marcin-chwedczuk.github.io/break-out-of-code-block-in-java-and-javascript – csharpfolk

Odpowiedz

664

Podobnie jak Perl,

loop1: 
    for (var i in set1) { 
loop2: 
     for (var j in set2) { 
loop3: 
      for (var k in set3) { 
       break loop2; // breaks out of loop3 and loop2 
      } 
     } 
    } 

jak określono w sekcji EMCA-262 12.12. [MDN Docs]

W przeciwieństwie do C, etykiety te mogą być używane tylko dla continue i break, ponieważ Javascript nie ma goto.

+0

Interesujące! Nie wiedziałem, że możesz się włamać do wytwórni. –

+5

Czy jest to "zerwanie * z * etykietą"? Wygląda bardziej jak nazywanie bloku z n opcjonalnie zagnieżdżonymi pętlami, a następnie przerwanie "przechodzenia przez blok" przez wywołanie break {label}. –

+6

@NielsBom Masz rację: wychodzi z pętli z podaną etykietą. Nie "goto" na etykiecie. Zasadniczo nazywasz pętlę i mówisz, że chcę wyrwać się z takich pętli. – devios1

110

Zawiń to w funkcję, a następnie po prostu return.

+0

I ' nie jestem pytającym, ale zgaduję, że to łatwe? efhemient's jest tak samo ważny, choć syntaktycznie nie lubię takich etykiet. Wygląda trochę jak GOTO - choć nie chcę tutaj otwierać puszki z robakami. – swilliams

+9

Wybieram akceptację tej odpowiedzi, ponieważ jest prosta i może być zaimplementowana w elegancki sposób. Absolutnie nienawidzę GOTO i uważam je za złą praktykę (* można otworzyć *), Ephemient jest zbyt blisko jednego. ; o) –

+12

IMO, GOTO są w porządku, o ile nie przerywają strukturyzacji. Ale każdemu z nich! – ephemient

35
var str = ""; 
for (var x = 0; x < 3; x++) { 
    (function() { // here's an anonymous function 
     for (var y = 0; y < 3; y++) { 
      for (var z = 0; z < 3; z++) { 
       // you have access to 'x' because of closures 
       str += "x=" + x + " y=" + y + " z=" + z + "<br />"; 
       if (x == z && z == 2) { 
        return; 
       } 
      } 
     } 
    })(); // here, you execute your anonymous function 
} 

Jak to jest? :)

+2

Doszedłem do wniosku, że to właśnie robił swilliams pod numerem –

+0

. Dzięki za pisanie tego w taki sposób. Zawsze zapominam, że mogę zawinąć dowolny dowolny kod w anonimową funkcję i zadzwonić. – Emily

+17

To dodaje znaczny koszt runtime, jeśli pętla jest duża - nowy kontekst wykonania dla funkcji musi zostać utworzony (i w pewnym momencie uwolniony przez GC) przez interpreter JavaScript/kompilator (lub "compreter" w tych dniach, mieszanka obu) ZA KAŻDYM RAZEM. –

65

Jestem trochę późno do partii, ale po to podejście język-agnostyk, który nie korzysta z goto/etykiety lub funkcja dozowania:

for (var x = Set1.length; x > 0; x--) 
{ 
    for (var y = Set2.length; y > 0; y--) 
    { 
     for (var z = Set3.length; z > 0; z--) 
     { 
      z = y = -1; // terminates second loop 
      // z = y = x = -1; // terminate first loop 
     } 
    } 
} 

Na plus płynie naturalnie który powinien zadowolić tłum nie-GOTO. Z drugiej strony wewnętrzna pętla musi zakończyć aktualną iterację przed zakończeniem, więc może nie mieć zastosowania w niektórych scenariuszach.

+2

nawias otwierający nie powinien znajdować się na nowych liniach, ponieważ implementacje js mogą wstawiać dwukropki w koniec poprzedniej linii. – Evgeny

+17

@Evgeny: podczas gdy niektóre przewodniki w stylu JavaScript wymagają otwierania zamków w celu przejścia na tę samą linię, nie jest niepoprawne, aby mieć je w nowej linii i nie ma niebezpieczeństwa, że ​​interpreter niejednoznacznie wstawi średnik. Zachowanie ASI jest dobrze zdefiniowane i nie ma tu zastosowania. –

+6

Tylko pamiętaj, aby wypowiedzieć się na temat tego podejścia. Nie jest od razu oczywiste, o co tu chodzi. – Qix

30

dość prosty

var a=[1,2,3]; 
var b=[4,5,6]; 
var breakCheck1=false; 

for (var i in a){ 
    for (var j in b){ 
     breakCheck1=true; 
     break; 
    } 
    if (breakCheck1) {break;} 
} 
+0

Zgadzam się, że jest to w rzeczywistości najlepsza funkcja, której nie skaluje się, zawijanie wszystkich pętli, jeśli nie skaluje, to znaczy utrudnia czytanie i debugowanie ... ta jest niesamowita. Możesz po prostu zadeklarować vars loop1, loop2, loop3 i dodać małą instrukcję na końcu. Aby złamać wiele pętli, musisz zrobić coś w stylu 'loop1 = loop2 = false;' –

-3
XXX.Validation = function() { 
    var ok = false; 
loop: 
    do { 
     for (...) { 
      while (...) { 
       if (...) { 
        break loop; // Exist the outermost do-while loop 
       } 
       if (...) { 
        continue; // skips current iteration in the while loop 
       } 
      } 
     } 
     if (...) { 
      break loop; 
     } 
     if (...) { 
      break loop; 
     } 
     if (...) { 
      break loop; 
     } 
     if (...) { 
      break loop; 
     } 
     ok = true; 
     break; 
    } while(true); 
    CleanupAndCallbackBeforeReturning(ok); 
    return ok; 
}; 
+9

To wygląda bardziej zagmatwująco niż oryginał. –

+19

Niczym postmodernistyczny wiersz – Digerkam

+0

Zagłosowano, ponieważ do pewnego czasu coraz częściej dochodzi do tego typu scenariuszy (w większości przypadków). – Cody

55

Zdaję sobie sprawę, że jest to bardzo stary temat, ale ponieważ mój standardowe podejście nie jest tu jeszcze, myślałem pisać go dla przyszłych pracowników Google.

var a, b, abort = false; 
for (a = 0; a < 10 && !abort; a++) { 
    for (b = 0; b < 10 && !abort; b++) { 
     if (condition) { 
      doSomeThing(); 
      abort = true; 
     } 
    } 
} 
+0

Jeśli "warunek" odnosi się do "prawda" w pierwszej iteracji zagnieżdżonej pętli, nadal wykonuje się pozostałe 10 iteracji, sprawdzając za każdym razem wartość 'abort'. Nie jest to problem z wydajnością dla 10 iteracji, ale byłby, powiedzmy, 10 000. – Robusto

+7

Nie, wychodzi z obu pętli. Oto demonstrujący [skrzypce] (http://jsfiddle.net/zmkfY/). Bez względu na to, jaki stan ustawisz, wyjdzie po tym, jak zostanie spełniony. – zord

+2

Optymalizacja polegałaby na dodaniu przerwy; po ustawieniu abort = true; i usuwanie! sprawdzanie warunków abortu z ostatniej pętli. – xer21

11

Nie używaj żadnych przerw, nie przerywaj flag i nie sprawdzaj żadnych dodatkowych warunków. Ta wersja po prostu atakuje zmienne pętli (czyni je Number.MAX_VALUE) po spełnieniu warunku i wymusza eleganckie zakończenie wszystkich pętli.

// No breaks needed 
for (var i = 0; i < 10; i++) { 
    for (var j = 0; j < 10; j++) { 
    if (condition) { 
     console.log("condition met"); 
     i = j = Number.MAX_VALUE; // Blast the loop variables 
    } 
    } 
} 

Było odpowiedź podobna do odlicza-owski typu pętli zagnieżdżonych, ale to działa na zwiększający typu pętli zagnieżdżonych bez konieczności rozważyć wartość zakończenia każdej pętli dla prostych pętli.

Inny przykład:

// No breaks needed 
for (var i = 0; i < 89; i++) { 
    for (var j = 0; j < 1002; j++) { 
    for (var k = 0; k < 16; k++) { 
     for (var l = 0; l < 2382; l++) { 
     if (condition) { 
      console.log("condition met"); 
      i = j = k = l = Number.MAX_VALUE; // Blast the loop variables 
     } 
     } 
    } 
    } 
} 
3

Jeśli używasz coffeescript, jest wygodna "do" słów kluczowych, które ułatwia zdefiniowanie i natychmiast wykonać anonimową funkcję:

do -> 
    for a in first_loop 
    for b in second_loop 
     if condition(...) 
     return 

... więc możesz po prostu użyć "return", aby wydostać się z pętli.

+0

To nie to samo. Mój oryginalny przykład ma trzy pętle 'for', a nie dwie. –

2

Pomyślałem, że przedstawię podejście funkcjonalno-programistyczne. Możesz przerwać zagnieżdżone funkcje Array.prototype.some() i/lub Array.prototype.every(), tak jak w moich rozwiązaniach. Dodatkową korzyścią tego podejścia jest to, żewylicza tylko własne właściwości wyliczalne obiektu, podczas gdy "a for-in loop enumerates properties in the prototype chain as well".

Blisko Rozwiązanie OP:

Args.forEach(function (arg) { 
     // This guard is not necessary, 
     // since writing an empty string to document would not change it. 
     if (!getAnchorTag(arg)) 
      return; 

     document.write(getAnchorTag(arg)); 
    }); 

    function getAnchorTag (name) { 
     var res = ''; 

     Object.keys(Navigation.Headings).some(function (Heading) { 
      return Object.keys(Navigation.Headings[Heading]).some(function (Item) { 
       if (name == Navigation.Headings[Heading][Item].Name) { 
        res = ("<a href=\"" 
           + Navigation.Headings[Heading][Item].URL + "\">" 
           + Navigation.Headings[Heading][Item].Name + "</a> : "); 
        return true; 
       } 
      }); 
     }); 

     return res; 
    } 

rozwiązanie, które zmniejsza Iterowanie nad pozycjami/pozycje:

var remainingArgs = Args.slice(0); 

    Object.keys(Navigation.Headings).some(function (Heading) { 
     return Object.keys(Navigation.Headings[Heading]).some(function (Item) { 
      var i = remainingArgs.indexOf(Navigation.Headings[Heading][Item].Name); 

      if (i === -1) 
       return; 

      document.write("<a href=\"" 
             + Navigation.Headings[Heading][Item].URL + "\">" 
             + Navigation.Headings[Heading][Item].Name + "</a> : "); 
      remainingArgs.splice(i, 1); 

      if (remainingArgs.length === 0) 
       return true; 
      } 
     }); 
    }); 
-3

najlepszym sposobem jest -
1) sortuj po obu macierzy, które są wykorzystywane w pierwszej i drugiej pętli.
2) jeśli element jest dopasowany, przerwij pętlę wewnętrzną i przytrzymaj wartość indeksu.
3) po uruchomieniu następnej iteracji uruchom pętlę wewnętrzną z wartością indeksu podtrzymania.

-1

Znam ten został poproszony 8 lat temu, ale w ES6 mamy pętlę for...of, który umożliwia korzystanie z standardowej przerwy funkcjonalności:

for (let item of items) { 
    if (item.id === id) { 
    //do something cool 
    break; 
    } 
} 
+10

Więc jak wyrwać się z dwóch pętli w ten sposób? Właśnie o to chodzi. –

1

Jak o popychanie pętle do granic końcowych

for(var a=0; a<data_a.length; a++){ 
     for(var b=0; b<data_b.length; b++){ 
      for(var c=0; c<data_c.length; c++){ 
       for(var d=0; d<data_d.length; d++){ 
       a = data_a.length; 
       b = data_b.length; 
       c = data_b.length; 
       d = data_d.length; 
      } 
     } 
     } 
    } 
+0

Myślę, że [odpowiedź Drakesa] (https://stackoverflow.com/a/30158489/4475014) ma tę samą logikę w bardziej zwięzły i jasny sposób. –

+0

absolutnie genialny! – geoyws

Powiązane problemy