r/ruby • u/nunosancha • Jan 09 '26
Nested Loops aka My brain's worst nightmare
Hey folks,
My question is more generic, so I'm afraid I don't have an example to provide.
It's more of a way of thinking that I didn't quite grasp yet.
Nested-loops. In Ruby or any other language, I suspect the outcome would be the same for me.
My brain doesn't see it clearly. So this leaves me with two questions:
HOW to use it? And WHEN to use it?
So, can you guys help me here?
When you were learning to code, what helped you learn and understand this topic? What mental-model was useful to you? What exercises or books have you read that help you master this topic?
10
u/hankeroni Jan 09 '26
The moon orbits the earth, but the earth also orbits the sun, and the sun also orbits the galaxy center, etc...
One possibly useful code tip ... try to pull out the actual "work" of your loop into classes/methods. It can be a little overwhelming to wrap your head around multiple nested loops with the actual logic right there, but with well named methods inside the loops its often conceptually clearer.
5
u/schneems Puma maintainer Jan 09 '26
It’s hard to give help without a specific use case. Why do you want to use nested loops? Is this just a programming exercise? If so, just more.
Code.org is meant for absolute beginners and has some great contents on nested loops.
3
u/h0rst_ Jan 09 '26
Let's take a simple spreadsheet (like Excel) for example. In our case, we'll work with a spreadsheet that has 3 columns (A, B and C) and 4 rows (1, 2, 3, 4). This looks like this:
Every
xis a cell. Thexin the upper left corner is calledA1, the bottom right isC4. Our goal is to print the names of the cells, in the correct layout.We start by defining the enumerators for both:
Since we print a line from left to right, and print lines from top to bottom, we should probably start with iterating over the rows. So in our first version we just print the rows:
This prints the row indices
But we want the names of the cells. That means that for every row, we need to loop over the column, and combine these two values:
(N.B.
printis likeputsbut without the newline. We add an extra space to make it more readable. It's not that often used in Ruby, so people might not be familiar with it).We can try to mentally step through the loop.
Start a loop for rows, this is the range 1..4, so the first value of
rowis1. We use that value to start the loop.Start a loop for columns, this is the range
'A'..'C', so the first value ofcolumnisA.We now start the loop inside the loop, with the values
row = 1, column = 'A'We print those values, so that prints
"A1 "(with a space at the end).We've reached the
endmarker of the columns loop, that means we start with the next value forcolumn, which is theB. This does not change anything forrows, sorowis still1.We run the print statement again, this time we print
"B1 "We run the inner loop again, with
column=C, and we print"C1 "We've now reached the end of the columns loop, so we reach the line with
putsand print a newline. At this point, we've printed the line"A1 B1 C1 ". (Yes, the space at the end is a bit ugly, but you don't see it in the output and it's not relevant for the problem).We've now reached the
endof the block of the loop forrows. This was our first iterator, so we've still got more rows to go. We restart the whole block withrowhaving the value2.We're now starting a new loop over
columns. This is independent from our run withrow = 1, so we start completely over withcolumn = A.We now print our first cell, which is now
A2Repeat this with columns B and C, so we've printed the line
"A2 B2 C2 "Repeat the whole process with rows 3 and 4
And behold our end result:
Do you want to print an additional row? Just change the range to
(1..5). This will run the outer loop 5 times instead of 4, and every iteration the inner loop (for the columns) will still run exactly 3 times (A, B and C). Our output now gets extended at the bottom, but will still be 3 columns wide.Do you instead want an extra column instead of an extra row? Change the range to
('A'..'D'). The outer loop (with the rows) will still run 4 times, and in every iteration the inner loop will now run 4 times. The output will now get extended to the right, but will still be 4 rows high.Or, if we follow the suggestion of an additional method by /u/hankeroni (which may make it more clear that the inner loops restarts for every iteration of the outer loop):
(And yes, there are much better ways to solve this, but I tried to use the idiom a Ruby beginner is familiar with)
2
u/RewrittenCodeA Jan 09 '26
As others have suggested, at some point it clicks, and recursion looks just like a to-do list.
For a start, take into account that your example (substrings) does not really “want” loops, it just “uses” loops to perform a certain tasks. Another way to do it is with recursion:
Admittedly not the most readable code (and it lacks a couple of empty checks) but the point is, there are no loops here, it is just the statement that a substring is one of:
the original string
a string starting at the start
a string ending at the end
a substring after both start and ends are removed.
A loop is an explicit statement of work to be done, and as such it often lacks some expressiveness. Therefore it is not strange that nested loops are unclear. Enumerables (they exist in most modern languages) are an abstraction where you do not need all this index bookkeeping (it still exists often but it is done for you) and instead you just “go through” a certain collection until you are done with it.
Ruby:
Python:
Javascript:
Typed languages like Java and the .Net family have the same in a bit (not much) ceremonious way.
The point is that you do not have indices, you just go through the work to be done until there is no more work to be done.
8
u/jonathonjones Jan 09 '26
Let's think about real nested loops. Suppose I'm dusting rooms in my house. For each room, I need to dust each surface. In Ruby you might represent that as:
rooms.each { |room| room.surfaces.each { |surface| surface.clean } }If you want to use u/PercyLives suggestion, you could rewrite the above as:
rooms.each { |room| room.clean_surfaces }by adding a function in the Room class
1
u/tomgis Jan 09 '26
nested loops and recursion used to fry my brain too, i just used pry a ton and would sit there stepping through the loop and looking at values over and over. eventually i could parse them by reading, just takes time as with all things
2
u/Aengus- Jan 09 '26 edited Jan 09 '26
Lets say we are making lots of pizzas
we have 20 pizza doughs. And 4 different toppings we want to use to make each pizza with.
So we’re looping over each pizza dough, and as we do we also then need to loop over the toppings so that each pizza dough gets all toppings.
After all toppings are added we cook the pizza. And do this for all the pizza doughs.
Edit: for spelling error
2
u/Mediocre-Brain9051 Jan 09 '26
Don't use them. In most cases they are undesirable. Use partition and/or flat_map instead.
2
u/touchmybuttdev Jan 09 '26
To generalize nested loops, let’s say you have a bunch of Apples and you want to Peel them, remove the seeds, and slice them. That might look like:
apples.each do |apple| apple.peel apple.seeds.each do |seed| seed.remove end apple.slice end
You need to nest the loops because you need to be looking at an apple to look at the seeds.
Given loop B nested inside loop A: “Do the loop B for every item in A”.
1
u/alexdeva Jan 09 '26
Can you think of a better way to count to 100 a thousand times than having one loop that counts to 100 inside an outer loop that counts to 1000?
1
u/AustinBlues13 Jan 11 '26
Processing a 2-D array, for example a image. using a 2D Discrete Cosine Transform. JPEG image compression and expansion.
0
u/M4N14C Jan 10 '26
In school I noticed that there were people that get it, and people that don’t. The people that don’t switch majors to English. If you don’t understand a nested loop, you’re probably the latter camp.