ChatGPT解决这个技术问题 Extra ChatGPT

What's the best way to break from nested loops in JavaScript?

What's the best way to break from nested loops 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.
         }
      }
   }
}
Here is good example of breaking out of loops and out of blocks of code: marcin-chwedczuk.github.io/…

e
ephemient

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

as defined in EMCA-262 section 12.12. [MDN Docs]

Unlike C, these labels can only be used for continue and break, as Javascript does not have goto.


@NielsBom You are correct: it breaks out of the loop with the given label. It doesn't goto the label. You are essentially naming a loop and saying I want to break out of suchandsuch a loop.
WTF why haven't I seen this being used somewhere in my 3 years with JavaScript :/..
MDN says "avoid using labels" purely on readability grounds. Why is it not 'readable'? Because nobody uses them, of course. But why don't they use them? ...
@SeantheBean Done. This does seem like the more straightforward answer and not open to abuse because it's only available to continue and break.
@JérémyPouyet - Your logic for down voting is inane and unwarranted. It answer the OP's question perfectly. The question is not concerned with your opinions regarding legibility. Please reconsider your approach to assisting the community.
C
CloudyMarble

Wrap that up in a function and then just return.


I choose to accept this answer because it is simple and can be implemented in an elegant fashion. I absolutely hate GOTO's and consider them bad practice (can open), Ephemient's is too near one. ;o)
IMO, GOTO's are fine as long as they don't break structuring. But to each their own!
Labels on for loops have absolutely nothing in common with GOTO except for their syntax. They are simply a matter to break from outer loops. You do not have any problem with breaking the innermost loop, do you? so why do you have a problem with breaking outer loops?
Please consider accepting the other answer. If not for Andrew Hedges comment (thanks btw.), I would have thought: ah, so javascript does not have that feature. And I bet many in the community might overlook the comment and think just the same.
Why doesn't Stack Overflow have a feature to let the community override the obviously wrong selected answer? :/
a
aleemb

I'm a little late to the party but the following is a language-agnostic approach which doesn't use GOTO/labels or function wrapping:

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

On the upside it flows naturally which should please the non-GOTO crowd. On the downside, the inner loop needs to complete the current iteration before terminating so it might not be applicable in some scenarios.


the opening brace should not be on new lines, because js implementations may insert a colon in the end of the preceding line.
@Evgeny: while some JavaScript style guides call for opening braces to go on the same line, it is not incorrect to have it on a new line and there is no danger of the interpreter ambiguously inserting a semicolon. The behavior of ASI is well defined and does not apply here.
Just make sure to comment the hell out of this approach. It's not immediately obvious what's going on here.
I may be missing something, but to get around the problem of the inner loop having to finish that iteration could you put in a break or continue immediately after you set z and y? I do like the idea of using the for loop's conditions to kick out. Elegant in its own way.
+1 for a novel approach! However, this won't help with for(var a in b){...} or for(var a of b){...} style for loops.
z
zord

I realize this is a really old topic, but since my standard approach is not here yet, I thought I post it for the future 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;
        }
    }
}

If the condition evaluates to true on the first iteration of the nested loop, you still run through the rest of the 10 iterations, checking the abort value each time. This is not a performance problem for 10 iterations, but it would be with, say, 10,000.
No, it's exiting from both loops. Here is the demonstrating fiddle. No matter what condition you set, it's exiting after it's met.
Optimization would be to add a break; after setting abort = true; and removing !abort condition check from the final loop.
I like this but I think in a general sense you would make lots of unnecessary processing - that is, for each iteration of every iterator evalueate abort and the expression. In simple scenarios that might be fine, but for huge loops with gazillion iterations that could be a problem
+1 for a novel approach! However, this won't help with for(var a in b){...} or for(var a of b){...} style for loops.
a
akinuri

Quite simple:

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

I agree this is actually the best, function one doesn't scale, wrapping all for loops in if also doesn't scale i.e. makes it hard to read and debug....this one is awesome. You can just declare vars loop1, loop2, loop3, and add little statement at the end. Also to break multiple loops you would need to do something like loop1=loop2=false;
I have used this arrangement and it works, without complicating it with useless functions. I only arrived here after searching for something to see if js has break 2; like in php.
h
harley.333
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
}

How's that? :)


I figured this is what swilliams was getting at
This adds significant runtime cost if the loop is large - a new execution context for the function must be created (and at some point freed by GC) by the Javascript interpreter/compiler (or, "compreter" these days, a mix of both) EVERY SINGLE TIME.
This is actually quite dangerous because some weird stuff can happen that you may not be expecting. In particular, because of the closure created with var x, if any logic within the loop references x at a later point in time (for example it defines an inner anonymous function that is saved and executed later), the value for x will be whatever it was at the end of the loop, not the index that function was defined during. (cont'd)
To get around this, you need to pass x as a parameter to your anonymous function so that it creates a new copy of it, which can then be referenced as a closure since it won't change from that point on. In short, I recommend ephemient's answer.
As well, I think the readability thing is complete crap. This is way more vague than a label. Labels are only seen as unreadable because nobody ever uses them.
D
Dan Bray

Here are five ways to break out of nested loops in JavaScript:

1) Set parent(s) loop to the end

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

2) Use label

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

3) Use variable

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 self executing function

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

5) Use regular function

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

@Wyck I can't agree enough! It's a shame javascript does not simply have a syntax break 2; like we have in PHP. No loop labels, no functions, no if-else checks, no tempering with / blasting of loop variables - just clean syntax!
Example 4 is nifty
@JayDadhania Sorry, your "clean" and "easy" syntax introduces bugs to our softwares. Explicit is better than implicit. I want to name my labels myself.
@EkremDinçel a syntax like break 2; does not exist in JS yet, so how could it introduce bugs? You want to label the loops manually? Sure, go ahead - I never said you shouldn't. Also, I in no way advised to manually label JS loops with 3,2,1 - JS does not allow manual labelling of loops with just numbers as of now. I only wished such a thing was implicitly available. Also, such a statement has been a core part of some very popular languages like PHP, and I haven't come across (m)any posts that "want to label the PHP loops manually because break 2; was hard to re-structure".
@JayDadhania It does not introduce bugs in JS for sure, it does not exists. But it would do if it exists, I think I explained a bit wrongly it in the above comment. The implicitly you want is a problem, people would use it if it exists in javascript whether you advise it or not. You are giving PHP as an example for this syntax, I think you should also notice the bad history of PHP. I heard they are fixing some things nowadays but it was a language that has so much spaghetti code in apps written with it for a long time, and there is a reason for that.
D
Drakes

How about using no breaks at all, no abort flags, and no extra condition checks. This version just blasts the loop variables (makes them Number.MAX_VALUE) when the condition is met and forces all the loops to terminate elegantly.

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

There was a similar-ish answer for decrementing-type nested loops, but this works for incrementing-type nested loops without needing to consider each loop's termination value for simple loops.

Another example:

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

N
Nick Perkins

If you use Coffeescript, there is a convenient "do" keyword that makes it easier to define and immediately execute an anonymous function:

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

...so you can simply use "return" to get out of the loops.


This isn't the same. My original example has three for loops not two.
u
user889030

How about pushing loops to their end limits

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

I think Drakes answer has the same logic in a more succinct and clear manner.
absolutely brilliant!
Z
Zachary Ryan Smith

I thought I'd show a functional-programming approach. You can break out of nested Array.prototype.some() and/or Array.prototype.every() functions, as in my solutions. An added benefit of this approach is that Object.keys() enumerates only an object's own enumerable properties, whereas "a for-in loop enumerates properties in the prototype chain as well".

Close to the OP's solution:

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

Solution that reduces iterating over the Headings/Items:

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

M
Matt Borja

Already mentioned previously by swilliams, but with an example below (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);
}

A
Azutanguy

Hmmm hi to the 10 years old party ?

Why not put some condition in your for ?

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

Like this you stop when you want

In my case, using Typescript, we can use some() which go through the array and stop when condition is met So my code become like this :

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

Like this, the loop stopped right after the condition is met

Reminder : This code run in TypeScript


T
Tom Chen

There are many excellent solutions above. IMO, if your break conditions are exceptions, you can use try-catch:

try{  
    for (var i in set1) {
        for (var j in set2) {
            for (var k in set3) {
                throw error;
            }
        }
    }
}catch (error) {

}

P
Pratik Khadtale

Assign the values which are in comparison condition

function test(){
    for(var i=0;i<10;i++)
    {
            for(var j=0;j<10;j++)
            {
                    if(somecondition)
                    {
                            //code to Break out of both loops here
                            i=10;
                            j=10;
                    }
                    
            }
    }

    //Continue from here

}


T
Timo

An example with for .. of, close to the example further up which checks for the abort condition:

test()
function test() {
  var arr = [1, 2, 3,]
  var abort = false;
  for (var elem of arr) {
    console.log(1, elem)

    for (var elem2 of arr) {
      if (elem2 == 2) abort = true;  
        if (!abort) {
            console.log(2, elem2)
        }
    }
  }
}

Condition 1 - outer loop - will always run

The top voted and accepted answer also works for this kind of for loop.

Result: the inner loop will run once as expected

1 1
2 1
1 2
1 3

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

This looks more confusing then the original.
Voted up because a do while is more becoming to this type of scenario (in most cases).
D
Deepak Karma

the best way is - 1) Sort the both array which are used in first and second loop. 2) if item matched then break the inner loop and hold the index value. 3) when start next iteration start inner loop with hold index value.