What's the best way to break from nested loops in JavaScript? [closed]
Asked Answered
C

18

644

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.
         }
      }
   }
}
Criss answered 8/10, 2008 at 14:47 Comment(1)
Here is good example of breaking out of loops and out of blocks of code: marcin-chwedczuk.github.io/…Luminance
C
1476

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.

Cursed answered 8/10, 2008 at 14:53 Comment(28)
Is it "breaking to a label"? It looks more like you're naming a block with n optionally-nested loops in it and then abort the "walking through the block" by calling break {label}.Entomophilous
@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.Evalyn
WTF why haven't I seen this being used somewhere in my 3 years with JavaScript :/..Establishment
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? ...Floss
It is possible to use loop: { /* loop here */ } for readability.Jiggle
This is bad practice. Consider answer with wrapping loop in a function strongly. Creates more readable and maintainable code.Unreeve
@Unreeve And functions cause considerable overhead. It depends on how they're used.Fulgurant
I don't think wrapping in a function creates more maintainable code versus just a few labels and breaks IMHO. What happens when you have multiply nested loops 3 or 4 levels deep -- 3 or 4 separate spaghetti functions?Hammon
Wrapping in a function in my opinion is way less readable then labeling and breaking out of loops this way, because most people are used to similar methods in different programming languages. I am now coding for about 3 years C#, JavaScript, Java and I find this approach way more readable than the answer from harley.333 down here.Rydder
Downvoting for a highly upvoted bad practice (i.e. this is go-to like programming in JS). Recommend reading JavaScript The Good Parts, and updating your answer.Actuality
I think this keeps the logic in one place, hence easier to debug. But that's coming from a JS noviceGorizia
Opinions about supposed 'best practices' aside, if I open up another programmer's code and they're breaking out of a nested loop--this is the answer I want to see. That is what I consider readable: How long does it take me to digest someone's code and make sense of it? Plus as many others have pointed out, it's performant. Give me this answer over any of the other more "correct"/convoluted solutions any day.Sandeesandeep
@Web_Designer I believe your comment is out of date. Nowhere in the MDN docs does it say to "avoid using labels". Please consider revising or removing your comment.Amazement
@GaryWilloughby Please consider making this the accepted answer. If I understand you correctly, your objection is that you "absolutely hate GOTO's and consider them bad practice", which is a highly subjective reason, and not very relevant since labels are fundamentally different from gotos. The number of upvotes for this answer is sufficient evidence that this is the answer most people reading this question are looking for. (And if I were a betting man, I'd also bet that means the majority of people don't consider using labels to be bad practice.)Amazement
@SeantheBean Done. This does seem like the more straightforward answer and not open to abuse because it's only available to continue and break.Criss
Downvote... There is so many ways to do this without using labels. ex: use another function and return or replace your for..in by another loop with an additional condition. If labels aren't used, it's not because it's not known, but because code quickly become unreadable!Pathless
@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.Pansophy
Not to mention that some problems are much cleaner with judicious label usage. Of course there are other ways to do it--that doesn't mean those other ways are better, cleaner, easier to understand, etc.Moue
If only JS could automatically label the loops as ..., 3, 2, 1 (with 1 being the innermost) - then we could use break 2; as we are used to in other languages.Intersection
OMG! This is like stepping back to 1995 and Visual Basic 5!Littleton
How have I never seen this before... Perfect. In the age of ES6, I've never even ran into a case where this made sense to do for literally years. That supercontinue is huge when filtering input.Orenorenburg
Does this work also for underscore _.each or _.some?Andrien
@JayDadhania Now this would make the code unreadable and hard to re-structure.Alcahest
@EkremDinçel I am not sure why a simple statement like break 2; would make the code unreadable or hard to re-structure.Intersection
@JayDadhania 1) I have to find and count for loops every time I see break x. 2) If I copy some parts of the inner loop and paste it to another while restructuring my code, there is a probablity that parser will not raise an error but I will have an logical bug. And if I will not get an error from the parser, I can forgot fixing the break numbers.Alcahest
I agree that such a bug could be introduced, but code readability and understandability is as much important as re-factoring. And it sure does make the code more readable than flag variables or temp functions.Intersection
Ha. I have not come across this - or needed it - in 25 years of JS experience! There's always something to learn in the JS world :)Trainload
@SamuelKatz because 3 years isn't as long as you think ;-)Dagny
J
237

Wrap that up in a function and then just return.

Jermyn answered 8/10, 2008 at 14:48 Comment(9)
I'm not the questioner, but my guess is that it's easy? ephemient's is just as valid, though syntactically I don't like labels like that. Feels kind of like GOTO - though I don't want to open that can of worms here.Jermyn
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)Criss
IMO, GOTO's are fine as long as they don't break structuring. But to each their own!Cursed
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?Vibrations
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.Vibrations
Why doesn't Stack Overflow have a feature to let the community override the obviously wrong selected answer? :/Georgetta
sorry I had to downvote. while this might be an elegant solution for some cases, it doesn't answer the question, even though there is a simple way to do it in javascript.Atalaya
This worked for me because the operation I was performing under my loop was time intensive, thus the boolean solution wasn't working for me. By the time the operation was completed, the loop had already completed its cycle and was only waiting for the operation that was on wait. UpvotedMicroampere
Best answer, really. Keep in mind that OP does not say what he/she does not need.Madeup
S
104

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.

Sham answered 7/4, 2009 at 22:11 Comment(7)
the opening brace should not be on new lines, because js implementations may insert a colon in the end of the preceding line.Heer
@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.Refugia
Just make sure to comment the hell out of this approach. It's not immediately obvious what's going on here.Fulgurant
Nice and simple answer. This should be considered as answer, as it doesnt strain the CPU intensive loops(which is a problem with using functions) or it doesn't use labels, which usually are not readable or shouldn't be used as some say. :)Forgery
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.Licha
I prefer labels, but props for a language-agnostic approach. Well done.Prinz
+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.Intersection
A
88

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;
        }
    }
}
Across answered 1/11, 2013 at 19:33 Comment(11)
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.Tarsier
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.Across
Optimization would be to add a break; after setting abort = true; and removing !abort condition check from the final loop.Sisile
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 problemFainthearted
Another nice language-agnostic approach, although less nuanced than the one above that allows breaking to any of the nested loops.Prinz
Clean and declarative approachBennett
+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.Intersection
Are you guys really arguing about whether checking a single boolean value 10000 times is fast or slow? try 100 million times /sighWayworn
@JayDadhania What can be used to break from nested loops in ECMA6 - for .. in and for ... of?Eureetloir
@Eureetloir Labelled loops would be the best choice IMO, or you can use a flag variable.Intersection
Yep, this is my go-to (pun not intended) method for breaking out of multiple loops, until I found out about loop labels today from Rust n found out JS has 'em too from this thread.Astroid
B
66

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;
}
Babirusa answered 16/7, 2009 at 15:22 Comment(2)
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;Teleprinter
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.Regality
C
55

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();
Cf answered 30/12, 2018 at 22:25 Comment(10)
@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!Intersection
Example 4 is niftyZischke
@JayDadhania Sorry, your "clean" and "easy" syntax introduces bugs to our softwares. Explicit is better than implicit. I want to name my labels myself.Alcahest
@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".Intersection
@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.Alcahest
The reason is that, language is allowing bad programmers to write bad code, maybe more than other languages. It shouldn't. It shouldn't have "features" like break n;. It shouldn't recommend usage of such features.Alcahest
@EkremDinçel Well the thing is that in absence of such "feature", people start developing their own logic. Consider example no. 1 and 3 in the answer above - hijacking a loop variable to the end or adding flag variables with if checks do nothing but make it harder to understand what the code does. break 2; (just like break label;) would be far more readable and understandable.Intersection
You see, even though loop labels exist in JS, most people still use other (hack-y) ways (hijacking / flag variables / temp functions) to break outer loops - you can see that from all the answers posted here. And while these hack-y ways are also possible in PHP, people there just prefer to use break n;.Intersection
@JayDadhania "You see, even though loop labels exist in JS, most people still use other (hack-y) ways" I think this is because peoples don't know the existance of this feature, I was also suprised when I see it. The accepted and most voted answer uses labels, and this shows that peoples prefer it when they are aware of it.Alcahest
Let us continue this discussion in chat.Alcahest
D
44
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? :)

Dovetail answered 8/10, 2008 at 19:42 Comment(7)
I figured this is what swilliams was getting atDovetail
Thanks for spelling it out explicitly like this. I always forget I can wrap any arbitrary piece of code up in an anonymous function and call it.Aten
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.Squashy
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)Evalyn
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.Evalyn
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.Fulgurant
@devios That's incorrect, if the logic in the loop references x and could be used later, as when setting an event handler, there would be a new closure created for that event handler, and that's the one that would have the problem. That is, the problem would be caused by the inner closure (wrapped around your event handler), not the outer closure (wrapped around the loop)Educated
L
17

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
        }
      }
    }
  }
}
Lenni answered 11/5, 2015 at 1:33 Comment(0)
M
3

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.

Mordacious answered 10/8, 2015 at 20:46 Comment(1)
This isn't the same. My original example has three for loops not two.Criss
M
3

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) {

}
Monto answered 7/1, 2021 at 2:10 Comment(1)
This is usually discouraged except for languages that have abstract exception handling such as python, since exceptions are usually costly JS loop increment + ~75%. If readable enough, I guess it's okay for non-libraries.Bonedry
O
2

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;
            }
        });
    });
Overlarge answered 24/8, 2015 at 18:36 Comment(0)
E
2

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;
            }
         }
       }
     }
Exegetics answered 26/9, 2017 at 10:58 Comment(2)
I think Drakes answer has the same logic in a more succinct and clear manner.Mebane
absolutely brilliant!Uxoricide
L
2

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);
}
Lifesize answered 14/12, 2018 at 7:19 Comment(0)
W
1

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

Wessels answered 19/6, 2019 at 16:26 Comment(0)
T
1

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

}

Tragic answered 23/9, 2021 at 12:30 Comment(0)
E
0

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
Eureetloir answered 6/6, 2022 at 17:29 Comment(0)
U
-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;
};
Underthecounter answered 12/7, 2012 at 22:16 Comment(2)
This looks more confusing then the original.Amsden
Voted up because a do while is more becoming to this type of scenario (in most cases).Concert
C
-5

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.

Coates answered 23/6, 2016 at 17:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.