runtime
66 / 250
lesson 66 Built-ins & Sorting · intermediate

Walk lists side by side with zip

`zip` walks two or more lists in step, handing you one item from each. It stops at the shortest list, which is either convenient or a bug.

You'll be able to
  • Walk parallel lists with for a, b in zip(xs, ys) instead of indexing with range(len(…))
  • Number items from 1 with enumerate(items, start=1)
  • Know that zip silently stops at the shortest list, and use strict=True or a length check when that would hide a bug
The idea

Data often arrives as parallel lists: one list of station names and another of the day's highs, where position 0 of one belongs with position 0 of the other. You could loop over range(len(stations)) and index both lists, but the index is only plumbing.

zip(a, b) hands you the pairs directly: (a[0], b[0]), then (a[1], b[1]), and so on. Unpack each pair in the for line. enumerate (see Loops & Iteration) works on any list too, and start=1 numbers from 1, the way people count.

Run each example to see what it prints. Running examples doesn't count toward progress.

example-1.py · Today's highs, numbered
stations = ["Great Falls Airport", "Boston Logan", "Atlanta Hartsfield"]
highs = [6.7, 2.2, 15.6]
for rank, (station, high) in enumerate(zip(stations, highs), start=1):
    print(f"{rank}. {station}: {high} C")

A spreadsheet export is the classic parallel case: one header row of column names, then rows of values in the same order. zip(header, row) pairs every name with its value, and dict(...) turns those pairs into a dictionary.

example-2.py · CSV rows as dictionaries
def rows_as_dicts(header, rows):
    return [dict(zip(header, row)) for row in rows]

header = ["flight", "origin", "destination"]
rows = [["UA2017", "LAS", "IAH"], ["MQ3443", "MCI", "MIA"]]
for record in rows_as_dicts(header, rows):
    print(record)

zip stops as soon as any list runs out, without a word. If one list is shorter because data went missing, the extra items are silently dropped and every later result can be wrong. When the lists must match, pass strict=True: zip then raises ValueError the moment the lengths turn out to differ.

example-3.py · strict=True catches a missing arrival time
scheduled = [300, 305, 313]
actual = [296, 312]
for planned, arrived in zip(scheduled, actual, strict=True):
    print(arrived - planned)

When to use it: any time two lists are matched by position: names and scores, a header and a row, scheduled and actual times. Mistakes to avoid: indexing with range(len(…)) when zip says it directly; and trusting zip with lists that might differ in length. Check len yourself, or use strict=True and let the error stop the program.

Review
  • for a, b in zip(xs, ys) walks parallel lists without an index; dict(zip(header, row)) builds a record.
  • enumerate(items, start=1) numbers from 1.
  • zip stops silently at the shortest list. Use strict=True or check lengths when a mismatch means bad data.
  • Next: sorted with a key, to order records by any field.
Practise this

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

Walk lists side by side with zip — Built-ins & Sorting · Runtime