Qual è il modo migliore per interrompere i loop nidificati in JavaScript?


448

Qual è il modo migliore per interrompere i loop nidificati in 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.
         }
      }
   }
}

Ecco un buon esempio di interruzione
csharpfolk,

Risposte:


1032

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

come definito nella sezione 12.12 dell'EMCA-262. [Documenti MDN]

A differenza di C, queste etichette possono essere utilizzate solo per continuee break, come Javascript non ha goto.


388
Perché non ho visto questo essere usato da qualche parte nei miei 3 anni con JavaScript: / ..
Salman von Abbas

40
MDN dice "evitare l'uso di etichette" esclusivamente per motivi di leggibilità. Perché non è "leggibile"? Perché nessuno li usa, ovviamente. Ma perché non li usano? ...
XML,

7
@Web_Designer Credo che il tuo commento non sia aggiornato. Da nessuna parte nei documenti MDN si dice "evita di usare le etichette". Ti preghiamo di considerare di rivedere o rimuovere il tuo commento.
Sean the Bean,

8
@SeantheBean Done. Questa sembra la risposta più semplice e non aperta agli abusi perché è disponibile solo per continuee break.
Gary Willoughby del

29
@ JérémyPouyet - La tua logica per il voto negativo è insensata e ingiustificata. Risponde perfettamente alla domanda del PO. La domanda non riguarda le tue opinioni sulla leggibilità. Si prega di riconsiderare il proprio approccio all'assistenza alla comunità.
Il Dembinski il

168

Avvolgilo in una funzione e poi solo return.


12
Ho scelto di accettare questa risposta perché è semplice e può essere implementata in modo elegante. Odio assolutamente i GOTO e li considero una cattiva pratica ( può aprire ), quella di Ephemient è troppo vicina. ; o)
Gary Willoughby

16
IMO, i GOTO vanno bene purché non rompano la strutturazione. Ma a ciascuno il suo!
effimero

31
Le etichette per i loop non hanno assolutamente nulla in comune con GOTO tranne la loro sintassi. Sono semplicemente una questione da rompere dagli anelli esterni. Non hai alcun problema a interrompere il ciclo più interno, vero? quindi perché hai un problema con la rottura dei loop esterni?
John Smith,

11
Ti preghiamo di considerare di accettare l'altra risposta. Se non fosse stato per il commento di Andrew Hedges (grazie a proposito), avrei pensato: ah, quindi JavaScript non ha questa funzione. E scommetto che molti nella community potrebbero trascurare il commento e pensare allo stesso modo.
John Smith,

11
Perché Stack Overflow non ha una funzione che consente alla community di ignorare la risposta selezionata ovviamente errata? : /
Matt Huggins il

85

Sono un po 'in ritardo alla festa, ma di seguito è riportato un approccio indipendente dal linguaggio che non utilizza GOTO / etichette o il wrapping delle funzioni:

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

Il lato positivo scorre naturalmente, il che dovrebbe piacere alla folla non GOTO. Sul lato negativo, il ciclo interno deve completare l'iterazione corrente prima di terminare, quindi potrebbe non essere applicabile in alcuni scenari.


2
la parentesi graffa di apertura non dovrebbe trovarsi su nuove righe, poiché le implementazioni di js possono inserire due punti alla fine della riga precedente.
Evgeny,

21
@Evgeny: mentre alcune guide di stile JavaScript richiedono l'apertura di parentesi graffe sulla stessa riga, non è errato averlo su una nuova riga e non c'è pericolo che l'interprete inserisca ambiguamente un punto e virgola. Il comportamento di ASI è ben definito e non si applica qui.
Jason Suárez,

9
Assicurati solo di commentare questo approccio. Non è immediatamente ovvio cosa sta succedendo qui.
Qix - MONICA È STATA MISTREATA il

1
Risposta semplice e piacevole. Questo dovrebbe essere considerato come una risposta, in quanto non sollecita i loop ad alta intensità di CPU (il che è un problema con l'uso delle funzioni) o non usa etichette, che di solito non sono leggibili o non dovrebbero essere usate come dicono alcuni. :)
Girish Sortur,

2
Forse mi manca qualcosa, ma per ovviare al problema del loop interno dovrei finire che l'iterazione potresti inserire in breako continueimmediatamente dopo aver impostato zey? Mi piace l'idea di usare le forcondizioni del loop per dare il calcio d'inizio. Elegante a modo suo.
Ben Sutton,

76

Mi rendo conto che questo è un argomento molto vecchio, ma dato che il mio approccio standard non è ancora qui, ho pensato di pubblicarlo per i futuri 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;
        }
    }
}

2
Se viene conditionvalutato truesulla prima iterazione del ciclo nidificato, si esegue comunque il resto delle 10 iterazioni, verificando il abortvalore ogni volta. Questo non è un problema di prestazioni per 10 iterazioni, ma sarebbe con, diciamo, 10.000.
Robusto,

7
No, sta uscendo da entrambi i loop. Ecco il violino dimostrativo . Indipendentemente dalle condizioni impostate, esce dopo essere stata soddisfatta.
zord

4
L'ottimizzazione sarebbe quella di aggiungere una pausa; dopo aver impostato abort = true; e rimuovendo! abort condition check dall'ultimo loop.
xer21

1
Mi piace questo, ma penso che, in generale, faresti un sacco di elaborazioni inutili, cioè per ogni iterazione di ogni iteratore valutatore aborte l'espressione. In scenari semplici che potrebbero andare bene, ma per enormi loop con iterazioni di gazillion che potrebbero essere un problema
Z. Khullah,

1
State davvero discutendo sul fatto che controllare un singolo valore booleano 10000 volte sia veloce o lento? prova 100 milioni di volte / sospiro
fabspro

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
}

Com'è quello? :)


2
Ho pensato che questo fosse ciò che Swilliams stava arrivando
harley.333

18
Questo aggiunge costi di runtime significativi se il loop è grande - un nuovo contesto di esecuzione per la funzione deve essere creato (e ad un certo punto liberato da GC) dall'interprete / compilatore Javascript (o, "compreter" in questi giorni, un mix di entrambi) OGNI SINGOLA VOLTA.
Mörre,

2
Questo è in realtà abbastanza pericoloso perché possono accadere cose strane che potresti non aspettarti. In particolare, a causa della chiusura creata con var x, se una logica all'interno del ciclo fa riferimento a x in un momento successivo (ad esempio definisce una funzione anonima interna che viene salvata ed eseguita in seguito), il valore per x sarà qualunque esso era alla fine del ciclo, non l'indice durante il quale era stata definita la funzione. (proseguendo)
devios1

1
Per ovviare a questo, è necessario passare xcome parametro alla funzione anonima in modo che ne crei una nuova copia, che può quindi essere indicata come chiusura poiché non cambierà da quel momento in poi. In breve, raccomando la risposta di effimero.
devios1

2
Inoltre, penso che la cosa della leggibilità sia una schifezza completa. Questo è molto più vago di un'etichetta. Le etichette sono viste solo come illeggibili perché nessuno le usa mai.
Qix - MONICA È STATA MISTREATA il

39

Abbastanza semplice:

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

Sono d'accordo che questo è in realtà il migliore, la funzione non si ridimensiona, avvolgendo tutto per i loop se anche non si ridimensiona cioè rende difficile la lettura e il debug .... questo è fantastico. Puoi semplicemente dichiarare vars loop1, loop2, loop3 e aggiungere una piccola istruzione alla fine. Inoltre, per interrompere più loop dovresti fare qualcosa del genereloop1=loop2=false;
Muhammad Umer,

22

Ecco cinque modi per uscire dai loop nidificati in JavaScript:

1) Impostare il loop parent (s) alla fine

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

2) Utilizzare l'etichetta

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

3) Usa variabile

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) Utilizzare la funzione di auto-esecuzione

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

5) Utilizzare la funzione regolare

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

1
@Wyck non posso essere abbastanza d'accordo! È un peccato che JavaScript non abbia semplicemente una sintassi break 2;come abbiamo in PHP. Nessuna etichetta di loop, nessuna funzione, nessun controllo if-else, nessun temperamento con / sabbiatura delle variabili di loop - solo una sintassi pulita!
Jay Dadhania,

1
L'esempio 4 è
elegante

14

Che ne dite di non usare nessuna pausa, nessun flag di interruzione e nessun controllo delle condizioni extra. Questa versione fa semplicemente saltare le variabili del loop (le rende Number.MAX_VALUE) quando la condizione è soddisfatta e forza tutti i loop a terminare in modo elegante.

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

Esisteva una risposta simile per i cicli annidati di tipo decrementante, ma questo funziona per i cicli annidati di tipo incrementale senza la necessità di considerare il valore di terminazione di ciascun ciclo per i cicli semplici.

Un altro esempio:

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

4

Che ne dici di spingere i loop fino ai loro limiti

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

1
Penso che la risposta di Drakes abbia la stessa logica in modo più conciso e chiaro.
Ingegnere Toast,

assolutamente brillante!
Geoyws,

3

Se usi Coffeescript, esiste una comoda parola chiave "do" che semplifica la definizione e l'esecuzione immediata di una funzione anonima:

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

... quindi puoi semplicemente usare "return" per uscire dai loop.


Questo non è lo stesso. Il mio esempio originale ha tre forloop non due.
Gary Willoughby,

2

Ho pensato di mostrare un approccio di programmazione funzionale. È possibile uscire dalle funzioni nidificate di Array.prototype.some () e / o Array.prototype.every (), come nelle mie soluzioni. Un ulteriore vantaggio di questo approccio è che Object.keys()elenca solo le proprietà enumerabili di un oggetto, mentre "un ciclo for-in elenca anche le proprietà nella catena di prototipi" .

Vicino alla soluzione del PO:

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

Soluzione che riduce l'iterazione su titoli / voci:

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

2

Già menzionato in precedenza da Swilliams , ma con un esempio di seguito (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);
}

0

Hmmm ciao alla festa di 10 anni?

Perché non mettere qualche condizione nel tuo per?

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

In questo modo ti fermi quando vuoi

Nel mio caso, usando Typescript, possiamo usare alcuni () che attraversano l'array e si fermano quando viene soddisfatta la condizione Quindi il mio codice diventa così:

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

In questo modo, il loop si è arrestato subito dopo aver soddisfatto la condizione

Promemoria: questo codice viene eseguito in TypeScript


-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
Questo sembra più confuso rispetto all'originale.
Cristiano Fontes,

21
Come una poesia postmoderna
Digerkam,

Votato perché un po 'di tempo sta diventando più per questo tipo di scenario (nella maggior parte dei casi).
Cody,

-4

il modo migliore è -
1) Ordinare entrambi gli array che vengono utilizzati nel primo e nel secondo ciclo.
2) se l'articolo corrisponde, interrompi il ciclo interno e mantieni il valore dell'indice.
3) quando inizia la successiva iterazione avvia il ciclo interno con il valore dell'indice di mantenimento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.