(Right now I'm mostly using C#. Ideas in other languages are welcome, but please translate them to C# if you can, and be explicit.)
Something I come across time and time again is a nested loop, searching through some 2D array to find an element (typically some object), which then has to be manipulated. So of course, once you find that object, you should break out of both loops so you don't needlessly continue the search for something you already found (especially in nested loops which can traversing exponentially huge arrays).
The following code is currently my preferred way of doing it:
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 Erik 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 pesky extra boolean!
Nevertheless, the search for alternative or better solutions goes on. Over the years I've tried many other ways, but find them not that great for one reason or another:
- Set
j
(the outer-loop iterator) to a value aboveheight
, which will trigger it to break automatically. Not ideal because sometimes you want to remember thei
andj
values where you found it. - Use
foreach
on the 2D array. Not ideal sinceforeach
won't let you manipulate the collection (delete or add to it, which very typically is the reason I search for the object anyway). - Just put the 2 loops in a function that does nothing but find and return
O
.return
effectively breaks both loops. Many times this is okay, but not always. I do this for very generic searches, but there are also quite a lot of "group searches" where I want to collectivize the traversing. In those cases, I find 2 or more objects (sometimes within the same 2D array), remember them, and only then break out of both loops. - Use a
goto
? (Whoa, could this be the only legit use of a goto? It's surprisingly more readable than theKeepLooping
flag, especially if we have 3 or more loops.) Not ideal because coworkers will scream bloody murder. And in C#, will there be correct garbage cleanup after thegoto
? - Throw a custom exception? idk, i've never tried it, but it looks less readable than my currently preferred way.
- Once you've found the right object, do all your object manipulation inside the inner loop, and then
return;
This could get messy fast. Sometimes the object manipulation involves 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 for
loop for iteration. However, some criticism has pointed out that the mod and division operators are pretty slow in comparison to just i++ or j++, so it could be slower (remember we are dealing with 2D arrays of who-knows-what size). Like i commented, there should be a way to get the division and remainder in one operation because I'm pretty sure the x86 assembly code has DIV opcodes that store the quotient and remainder in separate registers, all in one DIV instruction. But how to do/use that in C#, idk.
It would be nice if C# allowed you to name loops, such as L1
and L2
, and then do something like L1.break()
; regardless of which loop you're inside. Alas...it cannot be done in this language. (Could there be some secret way of doing this using macros?) Is there ever gonna be a C# 6.0 with this feature implemented?
Edit: in my opinion, I judge solutions on their elegance and speed. Remember we are dealing with nested loops, which can get exponentially huge. An extra operation or comparison could make a difference.
Okay, well, tell me your preferred way, especially if it's something not listed here.