Was ist der beste Weg, um verschachtelte Schleifen in JavaScript zu lösen?

448

Was ist der beste Weg, um verschachtelte Schleifen in Javascript zu lösen?

//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.
         }
      }
   }
}
Gary Willoughby
quelle
Hier ist ein gutes Beispiel für das Ausbrechen von Schleifen und Codeblöcken: marcin-chwedczuk.github.io/…
csharpfolk

Antworten:

1031

Genau wie 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
            }
        }
    }

wie in Abschnitt 12.12 von EMCA-262 definiert. [MDN-Dokumente]

Im Gegensatz zu C können diese Bezeichnungen nur für continueund verwendet werden break, da Javascript dies nicht hat goto.

kurzlebig
quelle
387
WTF, warum habe ich nicht gesehen, dass dies irgendwo in meinen 3 Jahren mit JavaScript verwendet wurde: / ..
Salman von Abbas
39
MDN sagt "Vermeiden Sie die Verwendung von Etiketten" nur aus Gründen der Lesbarkeit. Warum ist es nicht lesbar? Weil sie natürlich niemand benutzt. Aber warum benutzen sie sie nicht? ...
XML
7
@Web_Designer Ich glaube, Ihr Kommentar ist veraltet. Nirgendwo in den MDN-Dokumenten heißt es, "die Verwendung von Etiketten zu vermeiden". Bitte überlegen Sie, Ihren Kommentar zu überarbeiten oder zu entfernen.
Sean the Bean
8
@SeantheBean Fertig. Dies scheint die einfachere Antwort zu sein und ist nicht für Missbrauch offen, da sie nur für continueund verfügbar ist break.
Gary Willoughby
29
@ JérémyPouyet - Ihre Logik für das Abstimmen ist verrückt und ungerechtfertigt. Es beantwortet die Frage des OP perfekt. Die Frage betrifft nicht Ihre Meinung zur Lesbarkeit. Bitte überdenken Sie Ihren Ansatz zur Unterstützung der Community.
Der Dembinski
168

Wickeln Sie das in eine Funktion ein und dann einfach return.

Swilliams
quelle
12
Ich akzeptiere diese Antwort, weil sie einfach ist und auf elegante Weise umgesetzt werden kann. Ich hasse GOTOs absolut und halte sie für schlechte Praxis ( kann sich öffnen ), Ephemient ist zu nahe an einer. ; o)
Gary Willoughby
16
IMO, GOTOs sind in Ordnung, solange sie die Strukturierung nicht brechen. Aber für jeden das Richtige!
Ephemient
31
Labels on for-Schleifen haben mit GOTO absolut nichts gemeinsam, außer ihrer Syntax. Sie sind einfach eine Sache, um von äußeren Schleifen zu brechen. Sie haben kein Problem damit, die innerste Schleife zu durchbrechen, oder? Warum haben Sie ein Problem mit dem Brechen der äußeren Schleifen?
John Smith
11
Bitte akzeptieren Sie die andere Antwort. Ohne Andrew Hedges Kommentar (danke übrigens) hätte ich gedacht: ah, also hat Javascript diese Funktion nicht. Und ich wette, viele in der Community könnten den Kommentar übersehen und trotzdem denken.
John Smith
11
Warum verfügt Stack Overflow nicht über eine Funktion, mit der die Community die offensichtlich falsch ausgewählte Antwort überschreiben kann? : /
Matt Huggins
85

Ich bin etwas spät zur Party, aber das Folgende ist ein sprachunabhängiger Ansatz, bei dem GOTO / Labels oder Funktionsumbruch nicht verwendet werden:

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
      }
   }
}

Auf der Oberseite fließt es natürlich, was der Nicht-GOTO-Menge gefallen sollte. Auf der anderen Seite muss die innere Schleife die aktuelle Iteration vor dem Beenden abschließen, damit sie in einigen Szenarien möglicherweise nicht anwendbar ist.

Aleemb
quelle
2
Die öffnende Klammer sollte sich nicht in neuen Zeilen befinden, da js-Implementierungen möglicherweise einen Doppelpunkt am Ende der vorhergehenden Zeile einfügen.
Evgeny
21
@Evgeny: Während einige JavaScript-Styleguides das Öffnen von geschweiften Klammern in derselben Zeile fordern, ist es nicht falsch, sie in einer neuen Zeile zu haben, und es besteht keine Gefahr, dass der Interpreter ein Semikolon mehrdeutig einfügt. Das Verhalten von ASI ist gut definiert und gilt hier nicht.
Jason Suárez
9
Stellen Sie einfach sicher, dass Sie diesen Ansatz verdammt noch mal kommentieren. Es ist nicht sofort klar, was hier los ist.
Qix - MONICA wurde
1
Schöne und einfache Antwort. Dies sollte als Antwort betrachtet werden, da es die CPU-intensiven Schleifen nicht belastet (was ein Problem bei der Verwendung von Funktionen darstellt) oder keine Beschriftungen verwendet, die normalerweise nicht lesbar sind oder nicht verwendet werden sollten, wie manche sagen. :)
Girish Sortur
2
Ich vermisse vielleicht etwas, aber um das Problem zu umgehen, dass die innere Schleife diese Iteration beenden muss, können Sie ein breakoder continueunmittelbar nach dem Setzen von z und y einfügen? Ich mag die Idee, die forBedingungen der Schleife zum Ausstoßen zu nutzen. Auf seine Weise elegant.
Ben Sutton
76

Mir ist klar, dass dies ein wirklich altes Thema ist, aber da mein Standardansatz noch nicht hier ist, dachte ich, ich poste ihn für die zukünftigen Googler.

var a, b, abort = false;
for (a = 0; a < 10 && !abort; a++) {
    for (b = 0; b < 10 && !abort; b++) {
        if (condition) {
            doSomeThing();
            abort = true;
        }
    }
}
zord
quelle
2
Wenn der Wert bei der ersten Iteration der verschachtelten Schleife conditionausgewertet wird, werden truedie restlichen 10 Iterationen immer noch durchlaufen und der abortWert jedes Mal überprüft . Dies ist kein Leistungsproblem für 10 Iterationen, aber es wäre beispielsweise bei 10.000.
Robusto
7
Nein, es verlässt beide Schleifen. Hier ist die demonstrierende Geige . Egal welche Bedingung Sie einstellen, es wird beendet, nachdem es erfüllt ist.
Zord
4
Optimierung wäre, eine Pause hinzuzufügen; nach dem Setzen von abort = true; und Entfernen der Abbruchbedingung aus der letzten Schleife.
Xer21
1
Ich mag das, aber ich denke, im Allgemeinen würden Sie viel unnötige Verarbeitung vornehmen - das heißt, für jede Iteration jedes Iterator-Evalueate abortund des Ausdrucks. In einfachen Szenarien mag das in Ordnung sein, aber für große Schleifen mit millionenfachen Iterationen könnte das ein Problem sein
Z. Khullah
1
Streiten Sie sich wirklich darüber, ob die 10000-malige Überprüfung eines einzelnen Booleschen Werts schnell oder langsam ist? versuche es 100 Millionen Mal /
seufz
40
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
}

Wie ist das? :) :)

Harley.333
quelle
2
Ich dachte, das ist es, worauf Swilliams abzielt
Harley.333
18
Dies führt zu erheblichen Laufzeitkosten, wenn die Schleife groß ist - ein neuer Ausführungskontext für die Funktion muss vom Javascript-Interpreter / Compiler (oder heutzutage "Compreter", eine Mischung aus beiden) erstellt (und irgendwann von GC freigegeben) werden. JEDES MAL.
Mörre
2
Dies ist tatsächlich ziemlich gefährlich, da einige seltsame Dinge passieren können, die Sie möglicherweise nicht erwarten. Insbesondere aufgrund des mit var erstellten Abschlusses xist der Wert für x unabhängig davon, ob eine Logik innerhalb der Schleife zu einem späteren Zeitpunkt auf x verweist (z. B. definiert sie eine innere anonyme Funktion, die später gespeichert und ausgeführt wird) war am Ende der Schleife, nicht der Index, während dessen die Funktion definiert wurde. (Fortsetzung)
devios1
1
Um dies zu umgehen, müssen Sie xeinen Parameter an Ihre anonyme Funktion übergeben, damit eine neue Kopie davon erstellt wird, die dann als Abschluss referenziert werden kann , da sie sich ab diesem Zeitpunkt nicht mehr ändert. Kurz gesagt, ich empfehle die Antwort von Ephemient.
Devios1
2
Ich denke auch, dass die Sache mit der Lesbarkeit völliger Mist ist. Dies ist viel vager als ein Etikett. Etiketten werden nur als unlesbar angesehen, weil sie von niemandem verwendet werden.
Qix - MONICA wurde
39

Ziemlich einfach:

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;
}
Akinuri
quelle
Ich bin damit einverstanden, dass dies tatsächlich die beste Funktion ist, die man nicht skaliert und alle for-Schleifen einwickelt, wenn sie auch nicht skaliert, dh das Lesen und Debuggen erschwert. Diese Funktion ist fantastisch. Sie können einfach vars loop1, loop2, loop3 deklarieren und am Ende eine kleine Anweisung hinzufügen. Auch um mehrere Schleifen zu brechen, müssten Sie etwas tun wieloop1=loop2=false;
Muhammad Umer
22

Hier sind fünf Möglichkeiten, um aus verschachtelten Schleifen in JavaScript auszubrechen:

1) Setzen Sie die übergeordnete (n) Schleife (n) auf das Ende

for (i = 0; i < 5; i++)
{
    for (j = 0; j < 5; j++)
    {
        if (j === 2)
        {
            i = 5;
            break;
        }
    }
}

2) Etikett verwenden

exit_loops:
for (i = 0; i < 5; i++)
{
    for (j = 0; j < 5; j++)
    {
        if (j === 2)
            break exit_loops;
    }
}

3) Variable verwenden

var exit_loops = false;
for (i = 0; i < 5; i++)
{
    for (j = 0; j < 5; j++)
    {
        if (j === 2)
        {
            exit_loops = true;
            break;
        }
    }
    if (exit_loops)
        break;
}

4) Verwenden Sie die selbstausführende Funktion

(function()
{
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
             if (j === 2)
                 return;
        }
    }
})();

5) Verwenden Sie die reguläre Funktion

function nested_loops()
{
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
             if (j === 2)
                 return;
        }
    }
}
nested_loops();
Dan Bray
quelle
1
@ Wyck Ich kann nicht genug zustimmen! Es ist eine Schande, dass Javascript nicht einfach eine Syntax hat, break 2;wie wir sie in PHP haben. Keine Loop-Labels, keine Funktionen, keine If-else-Checks, kein Temperieren mit / Sprengen von Loop-Variablen - nur saubere Syntax!
Jay Dadhania
1
Beispiel 4 ist geschickt
leroyjenkinss24
14

Wie wäre es, wenn Sie überhaupt keine Unterbrechungen, keine Abbruchflags und keine zusätzlichen Bedingungsprüfungen verwenden? Diese Version sprengt nur die Schleifenvariablen (macht sie Number.MAX_VALUE), wenn die Bedingung erfüllt ist, und zwingt alle Schleifen, elegant zu enden.

// 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
    }
  }
}

Es gab eine ähnliche Antwort für verschachtelte Schleifen vom Dekrementiertyp, dies funktioniert jedoch für inkrementelle verschachtelte Schleifen, ohne den Abschlusswert jeder Schleife für einfache Schleifen berücksichtigen zu müssen.

Ein anderes Beispiel:

// 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
        }
      }
    }
  }
}
Drakes
quelle
4

Wie wäre es, Schleifen an ihre Endgrenzen zu bringen?

    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;
            }
         }
       }
     }
user889030
quelle
1
Ich denke, Drakes Antwort hat dieselbe Logik prägnanter und klarer.
Ingenieur Toast
absolut brilliant!
Geoyws
3

Wenn Sie Coffeescript verwenden, gibt es ein praktisches Schlüsselwort "do", mit dem Sie eine anonyme Funktion einfacher definieren und sofort ausführen können:

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

... also können Sie einfach "return" verwenden, um aus den Schleifen herauszukommen.

Nick Perkins
quelle
Das ist nicht dasselbe. Mein ursprüngliches Beispiel hat drei forSchleifen, nicht zwei.
Gary Willoughby
2

Ich dachte, ich würde einen funktionalen Programmieransatz zeigen. Sie können wie in meinen Lösungen aus verschachtelten Funktionen von Array.prototype.some () und / oder Array.prototype.every () ausbrechen. Ein zusätzlicher Vorteil dieses Ansatzes besteht darin, dass Object.keys()nur die eigenen aufzählbaren Eigenschaften eines Objekts aufgelistet werden, während "eine For-In-Schleife auch Eigenschaften in der Prototypkette auflistet" .

Nah an der Lösung des 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;
    }

Lösung, die das Durchlaufen der Überschriften / Elemente reduziert:

    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;
            }
        });
    });
Zachary Ryan Smith
quelle
2

Bereits zuvor von Swilliams erwähnt , aber mit einem Beispiel unten (Javascript):

// Function wrapping inner for loop
function CriteriaMatch(record, criteria) {
  for (var k in criteria) {
    if (!(k in record))
      return false;

    if (record[k] != criteria[k])
      return false;
  }

  return true;
}

// Outer for loop implementing continue if inner for loop returns false
var result = [];

for (var i = 0; i < _table.length; i++) {
  var r = _table[i];

  if (!CriteriaMatch(r[i], criteria))
    continue;

  result.add(r);
}
Matt Borja
quelle
0

Hmmm hallo zur 10 Jahre alten Party?

Warum nicht eine Bedingung in Ihr für setzen?

var condition = true
for (var i = 0 ; i < Args.length && condition ; i++) {
    for (var j = 0 ; j < Args[i].length && condition ; j++) {
        if (Args[i].obj[j] == "[condition]") {
            condition = false
        }
    }
}

So hörst du auf, wenn du willst

In meinem Fall können wir mit Typescript einige () verwenden, die das Array durchlaufen und anhalten, wenn die Bedingung erfüllt ist. Mein Code sieht also folgendermaßen aus:

Args.some((listObj) => {
    return listObj.some((obj) => {
        return !(obj == "[condition]")
    })
})

Auf diese Weise wurde die Schleife direkt nach Erfüllung der Bedingung gestoppt

Erinnerung: Dieser Code wird in TypeScript ausgeführt

Azutanguy
quelle
-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;
};
Triqui
quelle
9
Das sieht verwirrender aus als das Original.
Cristiano Fontes
21
Wie ein postmodernes Gedicht
Digerkam
Abgestimmt, weil eine Weile zu dieser Art von Szenario wird (in den meisten Fällen).
Cody
-4

Der beste Weg ist -
1) Sortieren Sie die beiden Arrays, die in der ersten und zweiten Schleife verwendet werden.
2) Wenn das Element übereinstimmt, unterbrechen Sie die innere Schleife und halten Sie den Indexwert.
3) Wenn Sie die nächste Iteration starten, starten Sie die innere Schleife mit dem Hold-Index-Wert.

Deepak Karma
quelle