Nested Loops

Concepts covered: Nested Loops

A loop placed inside another loop runs its whole course once for every step of the outer one. With 3 outer steps and 2 inner steps, the inner body runs 6 times, in a fixed order: both inner steps for outer step 1, then both for outer step 2, then both for outer step 3.

for i in range(1, 4):
    print("Outer:", i)
    for j in range(1, 3):
        print("  Inner:", j)
    print()
Outer: 1
  Inner: 1
  Inner: 2

Outer: 2
  Inner: 1
  Inner: 2

Outer: 3
  Inner: 1
  Inner: 2

Walking a grid

Tabular data often arrives as a list of rows in which every row is itself a list of cells. That shape is a grid. sales[1] is the second row, a list, and sales[1][2] indexes into that list for its third cell. The outer loop takes the rows and the inner loop takes the cells of the row in hand.

sales = [
    [12, 0, 7],
    [3, 9, 4],
    [0, 0, 15],
]
for store, row in enumerate(sales, start=1):
    total = 0
    for amount in row:
        total += amount
    print("store", store, "total", total)

Output:

store 1 total 19
store 2 total 16
store 3 total 15

Where total = 0 sits decides what it counts. Inside the outer loop it resets for each store. Moved above the outer loop, it never resets, and the 3 lines read 19, 35 and 50: running totals across all stores.

Wrapping both levels in enumerate gives every cell a pair of coordinates, which is how a data check reports where a bad value sits.

sales = [
    [12, 0, 7],
    [3, 9, 4],
    [0, 0, 15],
]
for r, row in enumerate(sales):
    for c, amount in enumerate(row):
        if amount == 0:
            print("empty cell at row", r, "col", c)

Output:

empty cell at row 0 col 1
empty cell at row 2 col 0
empty cell at row 2 col 1

Generating combinations

The second use of nested loops is pairing items. Looping over the same list twice makes every item meet every item: 4 teams give 16 pairs, including each team against itself and every match listed in both orders. To list each match once, start the inner loop 1 position past the outer one with range(i + 1, 4).

teams = ["ants", "bees", "cats", "dogs"]
for i in range(4):
    for j in range(i + 1, 4):
        print(teams[i], "vs", teams[j])

Output:

ants vs bees
ants vs cats
ants vs dogs
bees vs cats
bees vs dogs
cats vs dogs
iteams[i]j takesPairs printed
0ants1, 2, 33
1bees2, 32
2cats31
3dogsnothing0

The work a nested loop does is the outer size multiplied by the inner size. A 3 by 3 grid costs 9 inner steps. Comparing 2 lists of records item against item costs far more, and that comparison is where nested loops usually turn slow. A later lesson on joining records removes that inner loop entirely.

break exits only the inner loop

break ends the innermost loop it sits in. In a grid search, finding the target breaks the cell loop, and the row loop moves on to the next row unless something tells it to stop as well. The usual signal is a variable set at the moment of the find and checked right after the inner loop.

grid = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
target = 5
found_row = None
found_col = None
for r, row in enumerate(grid):
    for c, value in enumerate(row):
        if value == target:
            found_row = r
            found_col = c
            break
    if found_row is not None:
        break
print("Found", target, "at row", found_row, "col", found_col)
Found 5 at row 1 col 1

Every row of this grid holds a 5. What does the program print?

grid = [[1, 5], [5, 2], [3, 5]]
hits = 0
for row in grid:
    for value in row:
        if value == 5:
            hits += 1
            break
print(hits)

Stopping the whole scan takes 3 pieces: a variable set when the match is recorded, a break in the inner loop, and an if on that variable placed after the inner loop at the outer loop's indentation, holding a second break.

About this lesson

This section is part of the Loops: Intermediate lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.