Lesson 10 - Nested Loops & Logic
Learning Objective
Write loops inside loops to process grid-like data, and combine nested loops with conditionals to filter or react to what’s found.
Introduction
Many programming problems involve more than a single sequence of values.
Examples include:
- A multiplication table
- A game board
- An image made of pixels
- A spreadsheet
- Every possible pair of items in a list
These situations require nested loops, where one loop runs inside another.
Combined with conditional statements, nested loops allow programs to search, filter, compare, and process structured data efficiently.
In this lesson, you’ll learn:
- How nested loops work
- How to process two-dimensional data
- How to use
enumerate()in nested loops - How conditionals interact with nested loops
- How
breakandcontinueaffect loop behavior
What Is a Nested Loop?
A nested loop is simply a loop inside another loop.
for i in range(3):
for j in range(3):
print(i, j)Output:
0 0
0 1
0 2
1 0
1 1
1 2
2 0
2 1
2 2For every value of i, the entire inner loop runs from beginning to end.
Visualizing Nested Loops
Consider:
for row in range(2):
for col in range(3):
print(row, col)The execution order is:
row = 0
col = 0
col = 1
col = 2
row = 1
col = 0
col = 1
col = 2The outer loop advances only after the inner loop finishes completely.
Multiplication Table Example
Nested loops are often used to generate tables.
for i in range(1, 4):
for j in range(1, 4):
print(f"{i} x {j} = {i * j}")
print("---")This creates a simple 3×3 multiplication table.
Working with Grid Data
A grid is commonly represented as a list of lists.
grid = [
[3, 8, 2],
[7, 1, 9],
[4, 6, 5],
]Each inner list represents a row.
To visit every cell:
for row in grid:
for value in row:
print(value)Using enumerate()
enumerate() provides both the index and the value.
for index, value in enumerate(row):
print(index, value)Example:
row = [3, 8, 2]Output:
0 3
1 8
2 2This is useful when you need both the data and its position.
Code
# nested_loops.py
# multiplication table (3x3)
for i in range(1, 4):
for j in range(1, 4):
print(f"{i} x {j} = {i * j}")
print("---")
# grid search: find coordinates where a value exceeds a threshold
grid = [
[3, 8, 2],
[7, 1, 9],
[4, 6, 5],
]
threshold = 6
for row_index, row in enumerate(grid):
for col_index, value in enumerate(row):
if value > threshold:
print(
f"({row_index},{col_index}) = "
f"{value} exceeds threshold"
)Expected Output
Click to see expected output
1 x 1 = 1
1 x 2 = 2
1 x 3 = 3
---
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
---
3 x 1 = 3
3 x 2 = 6
3 x 3 = 9
---
(0,1) = 8 exceeds threshold
(1,0) = 7 exceeds threshold
(1,2) = 9 exceeds thresholdCombining Loops and Conditionals
Nested loops become powerful when combined with if statements.
Example:
for row in grid:
for value in row:
if value > 6:
print(value)Output:
8
7
9The condition filters out values that do not meet the requirement.
Finding Coordinates in a Grid
Because we used enumerate(), we know exactly where matching values are located.
(0,1) = 8Means:
Row 0
Column 1
Value 8This pattern is common in:
- Image processing
- Path planning
- Game development
- Robotics maps
- Spreadsheet analysis
Nested Loop Complexity
for i in range(100):Runs 100 times.
Single Loopfor i in range(100):
for j in range(100):Runs 10,000 times.
Nested LoopAs loops are nested deeper, the number of operations grows rapidly.
🔧 Under the Hood
How many times does the inner loop actually run?
For every iteration of the outer loop, the inner loop runs from beginning to end.
Example:
for i in range(3):
for j in range(3):
print(i, j)The inner print() executes:
3 × 3 = 9times.
Not:
3 + 3 = 6The multiplication happens because the entire inner loop repeats for every outer-loop iteration.
Growth Happens Quickly
Example:
for i in range(100):
for j in range(100):Results in:
100 × 100 = 10,000iterations.
Three nested loops of size 100 would produce:
100 × 100 × 100 = 1,000,000iterations.
This is why nested loops should be used carefully with large datasets.
Why Use enumerate()?
Without enumerate():
for value in row:you only know the value.
With:
for index, value in enumerate(row):you know:
- The value
- Its position
at the same time, without maintaining a separate counter variable.
Student Challenge
Challenge 1
Given a list of numbers [2, 4, 6, 8, 10] and a target sum of 12, write a program using nested loops to find every pair of numbers that adds up to the target.
Make sure your inner loop starts at i + 1 so you don’t pair a number with itself or print duplicate pairs (such as 2 + 10 and 10 + 2).
Hint
The inner loop starts at:
i + 1instead of:
0This prevents:
- Pairing a number with itself
- Checking the same pair twice
For example:
2 + 10and:
10 + 2would otherwise both be examined.
Click to see solution
# pair_sum_finder.py
numbers = [2, 4, 6, 8, 10]
target = 12
for i in range(len(numbers)):
for j in range(i + 1, len(numbers)):
if numbers[i] + numbers[j] == target:
print(
f"{numbers[i]} + "
f"{numbers[j]} = {target}"
)Challenge 2
Program your BonicBot to patrol a grid (2 rows by 3 columns) using nested for loops.
- Outer loop iterates through grid
rows, and inner loop iterates through gridcols. - Before driving
step_m(0.3m) forward into each cell, checkbot.get_min_obstacle_distance(). - If an obstacle is closer than
0.4meters, print a warning andcontinueto skip driving for that cell. - After finishing each row, turn the robot 90 degrees to face the next section.
- Experiment with
rows,cols, andstep_mto calculate the total travel distance when the path is clear.
Full movement details are available in the Python SDK reference.
Hint
If no obstacle is detected:
rows * cols * step_mgives the total forward distance traveled.
This works because the inner movement command runs exactly once for every grid cell.
Click to see solution
# challenge_grid_patrol.py
from bonicbot_bridge import BonicBot
rows, cols = 2, 3
step_m = 0.3
with BonicBot(host='192.168.0.188') as bot:
bot.wait_for_data(timeout=3.0)
for row in range(rows):
for col in range(cols):
distance = bot.get_min_obstacle_distance()
if distance is not None and distance < 0.4:
print(
f"Row {row}, Col {col}: "
f"obstacle at {distance:.2f} m — "
f"skipping cell."
)
continue
print(
f"Row {row}, Col {col}: "
f"moving forward {step_m} m."
)
bot.drive_distance(step_m, speed=0.2)
bot.rotate_angle(90, speed=45.0)
print(f"--- Row {row} complete ---")
bot.stop()Reflection Question
The obstacle check inside the grid patrol uses
continue, so it skips one cell but keeps patrolling the rest of the row. What would change about the pattern the robot drives if you replaced thatcontinuewith abreakinstead?
By the end of this lesson, you should be comfortable writing nested loops, processing grid-like data, using enumerate() to track positions, combining loops with conditional logic, and understanding how break and continue change the behavior of nested iterations.