Kodeclik Blog
How to iterate through multiple lists in Python
You will sometimes have multiple lists in Python and find a need to iterate through them simultaneously, in a coordinated manner. For instance, assume you have details of students in separate lists:
names = ['Tim','Elizabeth','Meredith']
ages = [12,15,16]
schools = ['Oakton Middle','Franklin High','Carson High']
You have one list containing names, one containing ages, and one representing the schools. The entries are expected to correspond to each other so Elizabeth is 15 years old and goes to Franklin High School. To print these correspondences, you need a way to systematically iterate through multiple lists in Python. Here are three ways to achieve this goal.
Method 1: Use the zip() function
The zip() function takes multiple iterables (here, lists) and returns tuples of corresponding entities one at a time. Since we are using three lists, the tuples returned by zip() contain three elements each. You can loop over them and perform your desired operation, like so:
for (n,a,s) in zip(names,ages,schools):
print(n + " is " + str(a) + " years old and goes to " + s + " School.")
The output is:
Tim is 12 years old and goes to Oakton Middle School.
Elizabeth is 15 years old and goes to Franklin High School.
Meredith is 16 years old and goes to Carson High School.
The zip() function expects the lists to be of the same length. Consider the following program:
names = ['Tim','Elizabeth','Meredith','John']
ages = [12,15,16,13]
schools = ['Oakton Middle','Franklin High','Carson High']
for (n,a,s) in zip(names,ages,schools):
print(n + " is " + str(a) + " years old and goes to " + s + " School.")
The output is:
Tim is 12 years old and goes to Oakton Middle School.
Elizabeth is 15 years old and goes to Franklin High School.
Meredith is 16 years old and goes to Carson High School.
Note that the output is still the same. This is because zip() discards any entries that do not have corresponding entries in other iterables. Thus, even though we have a fourth name (‘John’), a fourth age (13) we do not see a fourth line in the output because there are only three schools. In other words, the zip() stops at the shortest iterable.
Also note that zip() takes iterables as input. An iterable can be a list, tuple, string, etc. Here’s some updated code:
ids = "ABC"
names = ['Tim','Elizabeth','Meredith']
ages = (12,15,16)
schools = ['Oakton Middle','Franklin High','Carson High']
for (i,n,a,s) in zip(ids,names,ages,schools):
print("Student " + i + " named " + n + " is " + str(a)
+ " years old and goes to " + s + " School.")
Note that ids is a string, names and schools are lists, and ages is a tuple. All are iterables and zip can use all of them as inputs. In the print statement we mix elements from all four iterables to form our output:
Student A named Tim is 12 years old and goes to Oakton Middle School.
Student B named Elizabeth is 15 years old and goes to Franklin High School.
Student C named Meredith is 16 years old and goes to Carson High School.
Method 2: Use the itertools module
Our second approach allows the iterables to be of varying lengths. It uses the itertools module and specifically the function called “zip_longest” to create the same effect. Here’s an updated code:
import itertools
ids = "ABC"
names = ['Tim','Elizabeth','Meredith']
ages = (12,15,16)
schools = ['Oakton Middle','Franklin High','Carson High']
for (i,n,a,s) in itertools.zip_longest(ids,names,ages,schools):
print("Student " + i + " named " + n + " is " + str(a)
+ " years old and goes to " + s + " School.")
The output is, as before:
Student A named Tim is 12 years old and goes to Oakton Middle School.
Student B named Elizabeth is 15 years old and goes to Franklin High School.
Student C named Meredith is 16 years old and goes to Carson High School.
Note that the only differences are the import statement for “itertools” and the invocation of the zip function is changed to itertools.zip_longest().
An advantage of itertools.zip_longest() is that it will accept iterables of varying lengths. Let us see how that works. Here is an updated code where we increase the length of the ids string:
import itertools
ids = "ABCD"
names = ['Tim','Elizabeth','Meredith']
ages = (12,15,16)
schools = ['Oakton Middle','Franklin High','Carson High']
for (i,n,a,s) in itertools.zip_longest(ids,names,ages,schools):
print("Student " + i + " named " + n + " is "
+ str(a) + " years old and goes to " + s + " School.")
The output will be:
Student A named Tim is 12 years old and goes to Oakton Middle School.
Student B named Elizabeth is 15 years old and goes to Franklin High School.
Student C named Meredith is 16 years old and goes to Carson High School.
Traceback (most recent call last):
File "main.py", line 8, in <module>
print("Student " + i + " named " + n + " is "
+ str(a) + " years old and goes to " + s + " School.")
TypeError: can only concatenate str (not "NoneType") to str
Hmm. We receive an error. But note what the error says: it is trying to concatenate a “NoneType” to a string. To understand what is going on, let us not try to do any printing but instead inspect the results of the zipping operation:
import itertools
ids = "ABCD"
names = ['Tim','Elizabeth','Meredith']
ages = (12,15,16)
schools = ['Oakton Middle','Franklin High','Carson High']
print(itertools.zip_longest(ids,names,ages,schools))
The output will be (something like):
<itertools.zip_longest object at 0x7f365ffd4220>
This is not very informative. To understand the contents of the combined, i.e., zipped, iterable use the list() function:
import itertools
ids = "ABCD"
names = ['Tim','Elizabeth','Meredith']
ages = (12,15,16)
schools = ['Oakton Middle','Franklin High','Carson High']
print(list(itertools.zip_longest(ids,names,ages,schools)))
The output is now:
[('A', 'Tim', 12, 'Oakton Middle'), ('B', 'Elizabeth', 15,
'Franklin High'), ('C', 'Meredith', 16, 'Carson High'),
('D', None, None, None)]
Now we notice that for the fourth element of the “ids” string, the zip_longest() method has added “None” in place of elements which are missing from the other iterables. None is special type, not a string and thus the print() operation earlier has failed.
The solution to this problem is to replace “None” with a different default. This is specified by the parameter “fillvalue” in the function invocation, like so:
import itertools
ids = "ABCD"
names = ['Tim','Elizabeth','Meredith']
ages = (12,15,16)
schools = ['Oakton Middle','Franklin High','Carson High']
for (i,n,a,s) in itertools.zip_longest(ids,names,ages,schools,
fillvalue="None"):
print("Student " + i + " named " + n + " is "
+ str(a) + " years old and goes to " + s + " School.")
The output is:
Student A named Tim is 12 years old and goes to Oakton Middle School.
Student B named Elizabeth is 15 years old and goes to Franklin High School.
Student C named Meredith is 16 years old and goes to Carson High School.
Student D named None is None years old and goes to None School.
Method 3: Use the map() function
The final method we will explore is to essentially implement our own version of zip-like functionality using the map function.
The map function takes a function and applies it on one or more iterables. Looping over the iterables, map is passed the contents of the iterables in groups. So we can write a function that simply returns corresponding values as they are packaged. Here’s how this would work:
ids = "ABCD"
names = ['Tim','Elizabeth','Meredith']
ages = (12,15,16)
schools = ['Oakton Middle','Franklin High','Carson High']
def f(x,y,z,w):
return(x,y,z,w)
print(list(map(f, ids, names, ages, schools)))
In the final statement of this program, map applies “f” to four iterables which are returned as a tuple. We cannot directly print the map object but instead convert it into a list. The output is:
[('A', 'Tim', 12, 'Oakton Middle'), ('B', 'Elizabeth', 15, 'Franklin High'),
('C', 'Meredith', 16, 'Carson High')]
To obtain the same output as before, we update the f() function:
ids = "ABCD"
names = ['Tim','Elizabeth','Meredith']
ages = (12,15,16)
schools = ['Oakton Middle','Franklin High','Carson High']
def f(x,y,z,w):
return("Student " + x + " named " + y + " is "
+ str(z) + " years old and goes to " + w + " School.")
print(list(map(f, ids, names, ages, schools)))
The output will be:
['Student A named Tim is 12 years old and goes to Oakton Middle School.',
'Student B named Elizabeth is 15 years old and goes to Franklin High School.',
'Student C named Meredith is 16 years old and goes to Carson High School.']
We will leave it as an exercise to print the elements of this list one line at a time.
What will happen if the lists are of varying lengths. map() by default merely discards the extraneous entries.
If we update our program like so:
ids = "ABCDE"
names = ['Tim','Elizabeth','Meredith','John']
ages = (12,15,16)
schools = ['Oakton Middle','Franklin High','Carson High']
def f(x,y,z,w):
return("Student " + x + " named " + y + " is "
+ str(z) + " years old and goes to " + w + " School.")
print(list(map(f, ids, names, ages, schools)))
(Note that both ids and names have extra entries). The output will be the same as before:
['Student A named Tim is 12 years old and goes to Oakton Middle School.',
'Student B named Elizabeth is 15 years old and goes to Franklin High School.',
'Student C named Meredith is 16 years old and goes to Carson High School.']
So you have learnt three different ways to iterate through multiple lists simultaneously. Which one is your favorite?
For content similar to this blogpost, checkout our article on six different ways to do element-wise subtraction in Python lists.
Interested in more things Python? See our blogpost on Python's enumerate() capability. Also if you like Python+math content, see our blogpost on Magic Squares. Finally, master the Python print function!
Want to learn Python with us? Sign up for 1:1 or small group classes.