Is there a smart way to break out of nested loops?
(Right now I'm mostly using C #. Ideas in other languages ββare welcome, but please translate them to C # if you can, and explicitly.)
Something I run into over and over again is a nested loop that searches through some kind of 2D array to find an element (usually some object) that then needs to be manipulated. So, of course, once you spot this object, you must break out of both loops so you don't keep looking for what you've already found (especially in nested loops that can traverse exponentially huge arrays).
The following code is currently my preferred way:
Obj O = null;
bool KeepLooping = true;
for (int j = 0; j < height && KeepLooping; j++)
{
for (int i = 0; i < width; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
{
O = ObjArray[i, j]; // you found it, now remember it
KeepLooping = false; // clear the flag so the outer loop will break too
break;
}
}
}
Thanks to Eric Funkenbusch, it becomes much more elegant if we do this:
Obj O = null;
for (int j = 0; j < height && O == null; j++) // much, much better idea to check O for null in the outer loop
{
for (int i = 0; i < width; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
{
O = ObjArray[i, j]; // you found it, now remember it
break;
}
}
}
No more need for that annoying extra boolean!
Nevertheless, the search for alternative or better solutions continues. I have tried many other methods over the years, but I find they are not that great for one reason or another:
- Set
j
(the outer loop iterator) to a value higherheight
, which will cause it to break automatically. Not perfect because sometimes you want to remember the meaningi
andj
where you found it. - Use
foreach
in 2D array. Not ideal asforeach
it will not allow you to manipulate the collection (delete or add to it, which is very often the reason why I am looking for an object anyway). - Just put 2 loops in a function that does nothing but search and return
O
.return
effectively breaks both loops. This is normal many times, but not always. I do this for very general searches, but there are also quite a few "group searches" where I want to collectivize a move. In these cases, I find 2 or more objects (sometimes within the same 2D array), remember them and only then break out of both loops. - Use
goto
? (Whoa, could this be the only legal use of goto? It's surprisingly readable than a flagKeepLooping
, especially if we have three or more loops.) Not ideal, because colleagues will scream bloody murder. And in C #, will there be proper garbage cleanup aftergoto
? - Throw a custom exception? idk, I've never tried it, but it looks less readable than my preferred way.
- Once you've found the object you want, do all the manipulations on the object inside the inner loop, and then
return;
It can get messy . Sometimes manipulation of an object includes its own loops.
There is also a very clever 7th way , thanks to User_PWY:
int size = width*height; // save this so you dont have to keep remultiplying it every iteration
for (int i = 0; i < size; i++)
{
int x = i % width; // ingenious method here
int y = i / width; // ingenious method here
O = ObjArray[x, y];
if (O != null)
break; // woohoo!
}
This effectively compacts the 2D array into one loop for
for iteration. However, some critics have pointed out that mod and division operators are quite slow compared to i ++ or j ++ alone, so it might be slower (remember we're dealing with 2D arrays of someone who knows what size). As I commented, there must be a way to get the division and remainder in a single operation, because I am pretty sure the x86 assembly code has DIV opcodes that store the quotient and the rest in separate registers, all in one DIV instruction. But how to do / use it in C #, idk.
It would be nice if C # allowed you to name loops like L1
and L2
and then do something like L1.break()
; no matter which cycle you are inside. Alas ... it cannot be done in this language. ( Could there be a secret way to do this with macros? ). Will C # 6.0 ever have this feature?
Edit: In my opinion, I judge the solutions by their grace and speed. Remember that we are dealing with nested loops, which can be exponentially huge. Additional operation or comparison can make a difference.
Okay, well, tell me your preferred way, especially if not listed here.
source to share
for (int i = 0; i < width*height; i++)
{
int x=i%width
,y=i/width;
//dostuff
}
I like this way of accessing a 2d array.
comment 1)
There have been many comments worrying that the mod (%) operator can be expensive, but this is the whole operation we're talking about and I think it shouldn't have anything to do with another solution.
comment 2)
about the macro. I couldn't find the code, but managed to do it for a while.
#define FOR2DARRAY(WIDTH,HEIGHT)
\for (int i = 0, x = 0,y = 0; i < (WIDTH)*(HEIGHT); i++, x=i%(WIDTH),y=i/(HEIGHT))
source to share
Refactoring to avoid deep nesting, perhaps using a LINQ cleaver, is probably the best solution.
Goto
possible for this case (and this is essentially just a use case) if you can't find a better approach. The install icon with a readable name is likely to generate fewer questions / screams.
Not
- use exceptions for flow control
- change the loop variables. Figuring out what's going on is very difficult in this case. I would promise that most people will take
Goto
as the best approach.
source to share
goto
is the perfect solution for this, Microsoft even recommends it :
The goto statement transfers program control directly to the tagged statement.
A common use of goto is to transfer control to a particular label switch box or default label in a switch statement.
The goto statement is also useful for breaking out of deeply nested loops.
Regarding your question about destroying an object, once you are out of scope for the object, the garbage collector should mark it for destruction, but I can't say for sure if the behavior is the same if you use, for example, a using
around for
and goto
out of your loop , as opposed to the usual area.
source to share
Try the following:
Obj O = ObjArray.Cast<Obj>().FirstOrDefault(obj => obj != null && obj.property == search_value);
This will convert the 2D array Obj
to IEnumerable<Obj>
and give you the first one that suits your conditions. If none of them match, is Obj O
set to null
.
Take a look here: https://dotnetfiddle.net/dQTJbU
source to share
I may have missed it in the parameter list, but why can't you refactor the search into a method that returns your object?
private object FindObject(....)
{
for (int j = 0; j < height && KeepLooping; j++)
{
for (int i = 0; i < width; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property = search_value)
{
return ObjArray[i, j]; // you found it, now remember it
}
}
}
return null;
}
source to share
There are many ways to do this. In your example, I would do it like this:
Obj O = null;
for (int j = 0; j < height && O == null; j++)
{
for (int i = 0; i < width; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
{
O = ObjArray[i, j]; // you found it, now remember it
break;
}
}
}
In this case, the KeepLooping variable is unnecessary, and therefore a simple break statement works very elegantly.
You could even simplify this:
Obj O = null;
for (int j = 0; j < height && O == null; j++)
{
for (int i = 0; i < width && O == null; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
O = ObjArray[i, j]; // you found it, now remember it
}
}
No break is required now.
Of course, this may not work for all situations, but often you can use the result type as the state value.
FYI, the problem with changing the collection in foreach is not really a problem if you do it right. For example:
// Assumes ObjArray is [,] and not [][]
foreach (int item in ObjArray.ToList()) // makes a copy to iterate over
{
if (item != null && item.property == search_value) {
ObjArray[0,0] = item; // I can now modify this for no reason
break;
}
}
source to share
Not very different, but I prefer to use the boolean flag like this:
Obj O = null;
bool found = false;
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property = search_value)
{
O = ObjArray[i, j]; // you found it, now remember it
found = true; // clear the flag so the outer loop will break too
break;
}
}
if (found) break;
}
if (!found)
{
// The loop finished with no values found
}
else
{
// Do stuff here with the value found
}
source to share