Skip to Content
BonicBot A2DevelopmentPython ProgrammingLesson 10 - Nested Loops & Logic

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 break and continue affect 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 2

For 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 = 2

The 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 2

This 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 threshold

Combining 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 9

The 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) = 8

Means:

Row 0 Column 1 Value 8

This pattern is common in:

  • Image processing
  • Path planning
  • Game development
  • Robotics maps
  • Spreadsheet analysis

Nested Loop Complexity

As 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 = 9

times.

Not:

3 + 3 = 6

The 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,000

iterations.

Three nested loops of size 100 would produce:

100 × 100 × 100 = 1,000,000

iterations.

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 + 1

instead of:

0

This prevents:

  • Pairing a number with itself
  • Checking the same pair twice

For example:

2 + 10

and:

10 + 2

would 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 grid cols.
  • Before driving step_m (0.3m) forward into each cell, check bot.get_min_obstacle_distance().
  • If an obstacle is closer than 0.4 meters, print a warning and continue to skip driving for that cell.
  • After finishing each row, turn the robot 90 degrees to face the next section.
  • Experiment with rows, cols, and step_m to 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_m

gives 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 that continue with a break instead?


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.

Last updated on