Qual é a melhor maneira de interromper loops aninhados em JavaScript?

448

Qual é a melhor maneira de romper loops aninhados em 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.
         }
      }
   }
}
Gary Willoughby
fonte
Aqui está um bom exemplo de quebra de loops e de blocos de código: marcin-chwedczuk.github.io/…
csharpfolk

Respostas:

1031

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

conforme definido na seção 12.12 da EMCA-262. [Documentos da MDN]

Ao contrário de C, esses rótulos só podem ser usados continuee break, como o Javascript não possui goto.

efémero
fonte
387
WTF, por que eu não vi isso sendo usado em algum lugar nos meus 3 anos com JavaScript: / ..
Salman von Abbas
39
A MDN diz "evite usar etiquetas" apenas por motivos de legibilidade. Por que não é 'legível'? Porque ninguém os usa, é claro. Mas por que eles não os usam? ...
XML
7
@Web_Designer Acredito que seu comentário está desatualizado. Em nenhum lugar da documentação do MDN diz "evitar o uso de rótulos". Por favor, considere revisar ou remover seu comentário.
Sean Bean
8
@SeantheBean Concluído. Parece ser a resposta mais direta e não está aberta a abusos porque está disponível apenas para continuee break.
Gary Willoughby
29
@ JérémyPouyet - Sua lógica para voto negativo é insana e injustificada. Responde perfeitamente à pergunta do OP. A questão não está relacionada às suas opiniões sobre legibilidade. Por favor, reconsidere sua abordagem para ajudar a comunidade.
The Dembinski
168

Embrulhe isso em uma função e depois apenas return.

Swilliams
fonte
12
Eu escolho aceitar esta resposta porque é simples e pode ser implementada de maneira elegante. Eu absolutamente odeio GOTO e considero-os uma má prática ( pode abrir ), Ephemient é muito perto de um. ; o)
Gary Willough #
16
Na IMO, os GOTO estão bem desde que não quebrem a estrutura. Mas cada um na sua!
ephemient 10/10/08
31
Os rótulos para loops não têm absolutamente nada em comum com o GOTO, exceto por sua sintaxe. Eles são simplesmente uma questão de romper com laços externos. Você não tem nenhum problema em quebrar o loop mais interno, não é? Então, por que você tem um problema com a quebra de loops externos?
19414 John Smith
11
Por favor, considere aceitar a outra resposta. Se não fosse o comentário de Andrew Hedges (obrigado, btw.), Eu teria pensado: ah, então o javascript não possui esse recurso. E aposto que muitos na comunidade podem ignorar o comentário e pensar da mesma forma.
John Smith
11
Por que o Stack Overflow não tem um recurso para permitir que a comunidade substitua a resposta selecionada obviamente errada? : /
Matt Huggins
85

Estou um pouco atrasado para a festa, mas a seguir é uma abordagem independente de idioma que não usa GOTO / labels ou quebra de função:

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

No lado positivo, flui naturalmente, o que deve agradar à multidão que não é da GOTO. Por outro lado, o loop interno precisa concluir a iteração atual antes de terminar, para que não seja aplicável em alguns cenários.

aleemb
fonte
2
a chave de abertura não deve estar em novas linhas, porque as implementações js podem inserir dois pontos no final da linha anterior.
Evgeny
21
@ Evgeny: embora alguns guias de estilo JavaScript exijam que chaves de abertura sigam a mesma linha, não é incorreto colocá-lo em uma nova linha e não há perigo de o intérprete inserir ambiguamente um ponto e vírgula. O comportamento do ASI está bem definido e não se aplica aqui.
Jason Suárez
9
Apenas certifique-se de comentar bastante essa abordagem. Não é imediatamente óbvio o que está acontecendo aqui.
Qix - MONICA FOI ERRADA EM 04/11
1
Resposta agradável e simples. Isso deve ser considerado como resposta, pois não sobrecarrega os loops intensivos da CPU (o que é um problema no uso de funções) ou não usa etiquetas, que geralmente não são legíveis ou não devem ser usadas como dizem alguns. :)
Girish Sortur
2
Talvez esteja faltando alguma coisa, mas para contornar o problema do loop interno ter que concluir essa iteração, você poderia colocar um breakou continueimediatamente após definir z e y? Eu gosto da idéia de usar as forcondições do loop para começar. Elegante à sua maneira.
Ben Sutton
76

Sei que esse é um tópico muito antigo, mas como minha abordagem padrão ainda não está aqui, pensei em publicá-la para os futuros googlers.

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
fonte
2
Se for conditionavaliado truena primeira iteração do loop aninhado, você ainda executará o restante das 10 iterações, verificando o abortvalor a cada vez. Este não é um problema de desempenho para 10 iterações, mas seria com, por exemplo, 10.000.
Robusto
7
Não, está saindo dos dois loops. Aqui está o violino demonstrativo . Independentemente da condição definida, ela sai após ser atendida.
Zord
4
A otimização seria adicionar uma pausa; depois de definir abort = true; e remover a verificação da condição! abort do loop final.
Xer21 20/09/16
1
Eu gosto disso, mas acho que, em um sentido geral, você faria muito processamento desnecessário - ou seja, para cada iteração de cada iterador avaliado aborte a expressão. Em cenários simples que podem ser bons, mas para loops enormes com iterações com zilhões de
dólares
1
Vocês estão realmente discutindo se a verificação de um único valor booleano 10000 vezes é rápida ou lenta? tente 100 milhões de vezes / suspiro
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
}

Como é isso? :)

harley.333
fonte
2
Eu achei que era isso que Swilliams estava conseguindo
harley
18
Isso adiciona um custo de tempo de execução significativo se o loop for grande - um novo contexto de execução para a função deve ser criado (e em algum momento liberado pelo GC) pelo interpretador / compilador Javascript (ou "compreter" atualmente, uma mistura de ambos) CADA ÚNICO TEMPO.
18711
2
Isso é realmente muito perigoso, porque algumas coisas estranhas podem acontecer que você pode não estar esperando. Em particular, devido ao fechamento criado com var x, se alguma lógica dentro do loop fizer referência a x em um momento posterior (por exemplo, ela define uma função anônima interna que é salva e executada posteriormente), o valor de x será o que for estava no final do loop, não o índice em que a função foi definida durante. (continuação)
devios1
1
Para contornar este problema, você precisa passar xcomo um parâmetro para a sua função anônima para que ele cria uma nova cópia do mesmo, que pode então ser referenciado como um fechamento, uma vez que não vai mudar a partir desse ponto. Em suma, recomendo a resposta do epemiente.
Devios1 31/05
2
Também acho que a legibilidade é uma porcaria completa. Isso é muito mais vago do que um rótulo. Os rótulos são vistos apenas como ilegíveis porque ninguém os usa.
Qix - MONICA FOI ERRADA EM 04/11
39

Bem simples:

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
fonte
Concordo que este é realmente o melhor, função que não é escalável, agrupando todos os loops se também não escalar, ou seja, dificulta a leitura e a depuração ... este é incrível. Você pode apenas declarar vars loop1, loop2, loop3 e adicionar pouca instrução no final. Também para quebrar múltiplos loops que você precisa fazer algo comoloop1=loop2=false;
Muhammad Umer
22

Aqui estão cinco maneiras de interromper os loops aninhados no JavaScript:

1) Defina o loop pai (s) até o final

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

2) Use etiqueta

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

3) Use variável

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) Use a função de execução automática

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

5) Use a função regular

function nested_loops()
{
    for (i = 0; i < 5; i++)
    {
        for (j = 0; j < 5; j++)
        {
             if (j === 2)
                 return;
        }
    }
}
nested_loops();
Dan Bray
fonte
1
@ Wyck Eu não concordo o suficiente! É uma pena que o javascript não tenha simplesmente uma sintaxe break 2;como a que temos no PHP. Sem rótulos de loop, sem funções, sem verificações if-else, sem moderação com / explosão de variáveis ​​de loop - apenas sintaxe limpa!
Jay Dadhania
1
O exemplo 4 é bacana
leroyjenkinss24 08/01
14

Que tal usar sem interrupções, sem sinalizadores de cancelamento e sem verificações extras de condição. Esta versão apenas explode as variáveis ​​de loop (as cria Number.MAX_VALUE) quando a condição é atendida e força todos os loops a terminarem com elegância.

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

Houve uma resposta semelhante para os loops aninhados do tipo decrementar, mas isso funciona para loops aninhados do tipo incrementador sem a necessidade de considerar o valor de terminação de cada loop para loops simples.

Outro exemplo:

// 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
fonte
4

Que tal empurrar loops para seus limites finais

    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
fonte
1
Acho que a resposta de Drakes tem a mesma lógica de uma maneira mais sucinta e clara.
Engenheiro Toast
Absolutamente brilhante!
geoyws
3

Se você usa o Coffeescript, existe uma palavra-chave "do" conveniente que facilita a definição e a execução imediata de uma função anônima:

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

... para que você possa simplesmente usar "return" para sair dos loops.

Nick Perkins
fonte
Isto não é o mesmo. Meu exemplo original tem três forloops e não dois.
Gary Willoughby
2

Eu pensei em mostrar uma abordagem de programação funcional. Você pode interromper as funções Array.prototype.some () e / ou Array.prototype.every () aninhadas, como nas minhas soluções. Um benefício adicional dessa abordagem é que Object.keys()enumera apenas as propriedades enumeráveis ​​de um objeto, enquanto "um loop for-in também enumera propriedades na cadeia de protótipos" .

Perto da solução do 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;
    }

Solução que reduz a iteração sobre os títulos / itens:

    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
fonte
2

Já mencionado anteriormente por swilliams , mas com um exemplo abaixo (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
fonte
0

Hmmm oi para a festa de 10 anos?

Por que não colocar alguma condição no seu para?

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

Assim você para quando quiser

No meu caso, usando o Typecript, podemos usar alguns () que passam pela matriz e param quando a condição é atendida. Portanto, meu código se torna assim:

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

Assim, o loop parou logo após a condição ser atendida

Lembrete: esse código é executado no TypeScript

Azutanguy
fonte
-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
fonte
9
Isso parece mais confuso que o original.
Cristiano Fontes
21
Como um poema pós-moderna
Digerkam
Votada porque um tempo de espera está se tornando mais para esse tipo de cenário (na maioria dos casos).
Cody
-4

a melhor maneira é -
1) Classifique a matriz que é usada no primeiro e no segundo loop.
2) se o item corresponder, quebre o loop interno e mantenha o valor do índice.
3) quando iniciar a próxima iteração, inicie o loop interno com o valor de índice de espera.

Deepak Karma
fonte