zip() Parallel Iteration
Concepts covered: Parallel Iteration
Data often arrives column by column: a list of user IDs, a list of countries and a list of play counts, where position 2 in each list describes the same user. Reading a user means taking the same position from every list. With enumerate that is for i, user in enumerate(users): plus countries[i] and plays[i] in the body, which works and puts an index on every line that reads a column.
zip(users, countries) walks the lists in lockstep. Its first step pairs the first item of each list, its second step the second items, and so on, so for user, country in zip(users, countries): binds 1 name per list and needs no index. It accepts any number of lists, and the count of names after for has to match the count of lists passed in.
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
for name, age in zip(names, ages):
print(name + " is " + str(age) + " years old")Alice is 25 years old Bob is 30 years old Charlie is 35 years old
users = ["u7", "u12", "u31"]
countries = ["BR", "SE", "JP"]
plays = [140, 85, 302]
for user, country, count in zip(users, countries, plays):
print(user, country, count)Output:
u7 BR 140 u12 SE 85 u31 JP 302
- Counter plus indexes
- One list drives the loop and the rest are read by position
- Every column read is a [i] that can name the wrong list
- A short list fails with an IndexError partway through
- zip
- All lists advance together, 1 item each per step
- Each column gets its own name in the header
- A short list ends the loop early and raises nothing
The shortest list decides
zip stops the moment any of its lists runs out. It raises no error and prints no warning, and the leftover items in the longer lists are never visited.
names = ["Alice", "Bob", "Charlie", "Diana"]
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(name + ":", score)
print()
letters = ["a", "b"]
numbers = [1, 2, 3, 4, 5]
for letter, num in zip(letters, numbers):
print(letter, num)Alice: 85 Bob: 92 Charlie: 78 a 1 b 2
In a pipeline, that silence is the risk. Suppose the country lookup failed for the last user and countries came back 1 item short. A loop over zip(users, countries, plays) writes 2 rows where there should be 3, and nothing in the output shows that u31 went missing.
| Position | users | countries | plays | zip step |
|---|---|---|---|---|
| 0 | u7 | BR | 140 | 1 |
| 1 | u12 | SE | 85 | 2 |
| 2 | u31 | missing | 302 | never reached |
Positions and parallel values together
When the row number matters too, wrap the zip in enumerate: for i, (user, count) in enumerate(zip(users, plays)):. Each step of enumerate delivers a position and an item, and here the item is itself a pair made by zip. The parentheses copy that shape: i takes the position and (user, count) splits the inner pair. Without them, Python sees 3 names for a value with 2 parts and stops with ValueError: not enough values to unpack (expected 3, got 2).
users = ["u40", "u41", "u42"]
plays = [12, 0, 9]
for i, (user, count) in enumerate(zip(users, plays), start=1):
if count == 0:
print("row", i, "user", user, "has no plays")Output:
row 2 user u41 has no plays
Turning 2 lists into a dict
A frequent job for zip is building a lookup from 2 columns. The first list supplies the keys, the second supplies the values, and the loop assigns each pair.
users = ["u7", "u12", "u31"]
plays = [140, 85, 302]
plays_by_user = {}
for user, count in zip(users, plays):
plays_by_user[user] = count
print(plays_by_user)
print(plays_by_user["u12"])Output:
{'u7': 140, 'u12': 85, 'u31': 302}
85If a key appears twice in the first list, the later assignment overwrites the earlier one, and the dict ends up with fewer entries than the lists have items.
The names list has 4 items and the scores list has 3. What does this program print?
names = ["ana", "ben", "cal", "dee"]
scores = [3, 9, 4]
total = 0
for name, score in zip(names, scores):
total += score
print(total)- IndexError, since dee has no matching score
- 16, then a ValueError for the unpaired dee
- Nothing, since zip refuses lists of unequal length
- 16, from the 3 pairs zip makes before stopping
Building a lookup with zip takes 3 pieces: an empty dict created before the loop, a header with 1 name per list, and a body that assigns the second name's value under the first name as the key.
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.