Zip

Looping with indexes

Let’s say we have two lists that are related by their item indexes:

>>> group1 = ["Rita", "Derrick", "Stacey"]
>>> group2 = ["Fredrick", "Michael", "Priscilla"]

And we’d like to pair up people in each of these groups. We don’t know how many people there are, but we know that each list should have the same number.

How could we do this?

One way would be to get the indexes that represent each list and do a lookup into each:

>>> for i in range(len(group1)):
...     print(group1[i], "and", group2[i])
...
Rita and Fredrick
Derrick and Michael
Stacey and Priscilla

There’s a better way to get indexes though. What is it?

>>> for i, name1 in enumerate(group1):
...     print(name1, "and", group2[i])
...
Rita and Fredrick
Derrick and Michael
Stacey and Priscilla

It’s enumerate!

When you need to count upward while looping, you should reach for the enumerate function.

We don’t actually care about the index here though. The thing we care about doing is looping over two lists at the same time.

Python has something else that can help us do exactly that. It’s called zip:

>>> for name1, name2 in zip(group1, group2):
...     print(name1, "and", name2)
...
Rita and Fredrick
Derrick and Michael
Stacey and Priscilla

Python’s zip utility is the preferred way to loop over two iterables (lists, tuples, or anything else we can loop over) at the same time.

Looping over two lists at once

Let’s take a look at zip:

>>> zip([1, 2], [3, 4])
<zip object at 0x...>

zip returns an iterable object, but it’s not a list. Let’s turn it into a list so we can see what’s inside it:

>>> list(zip([1, 2], [3, 4]))
[(1, 3), (2, 4)]

So zip takes the first item from each list and puts them in a tuple, then takes the second item from each list and puts them in a tuple, and so on.

>>> list(zip([1, 2], [3, 4], [5, 6]))
[(1, 3, 5), (2, 4, 6)]

What happens if the lists we start with are not the same length?

>>> list(zip([1, 2], [3, 4, 5]))
[(1, 3), (2, 4)]

zip will stop once it reaches the end of the shortest iterable given to it.

Zip Exercises

Hint

If you get stuck for a minute or more, try searching Google or using help.

If you’re stuck for more than a few minutes, some of these links might be helpful for some of the exercises below:

Reverse Difference

This is the reverse_difference exercise we saw earlier in ranges.py. Edit the ranges.py file in the exercises directory to implement this exercise. To test it, run python test.py reverse_difference in your exercises directory.

The function reverse_difference accepts a list of numbers and returns a new copy of the list with the reverse of the list subtracted.

Re-write the code to use the zip function.

Example usage:

>>> from ranges import reverse_difference
>>> reverse_difference([9, 8, 7, 6])
[3, 1, -1, -3]
>>> reverse_difference([1, 2, 3, 4, 5])
[-4, -2, 0, 2, 4]
>>> reverse_difference([3, 2, 1, 0])
[3, 1, -1, -3]
>>> reverse_difference([0, 0])
[0, 0]

Note

Want to test this out manually (instead of using python test.py)?

You could could create a file called reverse_difference_test.py with your own test code:

from ranges import reverse_difference

print("Calling reverse_difference([9, 8, 7, 6])")
print("Expected: [3, 1, -1, -3]")
print("  Actual:", reverse_difference([9, 8, 7, 6]))

print()

print("Calling reverse_difference([1, 2, 3, 4, 5])")
print("Expected: [-4, -2, 0, 2, 4]")
print("  Actual:", reverse_difference([1, 2, 3, 4, 5]))

print()

print("Calling reverse_difference([3, 2, 1, 0])")
print("Expected: [3, 1, -1, -3]")
print("  Actual:", reverse_difference([3, 2, 1, 0]))

Then you can run that file to test your code:

$ python reverse_difference_test.py

Matrix Addition

This is the matrix_add exercise we saw earlier in ranges.py. Edit the ranges.py file in the exercises directory to implement this exercise. To test it, run python test.py matrix_add in your exercises directory.

The function matrix_add accepts two matrices (lists of lists of numbers) and returns one matrix that includes each corresponding number in the two lists added together.

You should assume the lists of lists provided will always be the same size/shape.

Re-write the function to use the zip function.

>>> from ranges import matrix_add
>>> m1 = [[1, 2], [3, 4]]
>>> m2 = [[5, 6], [7, 8]]
>>> matrix_add(m1, m2)
[[6, 8], [10, 12]]
>>> m1 = [[1, 2, 3], [0, 4, 2]]
>>> m2 = [[4, 2, 1], [5, 7, 0]]
>>> matrix_add(m1, m2)
[[5, 4, 4], [5, 11, 2]]

Parse two-line CSV data

This is the parse_row exercise in ranges.py. Edit the ranges.py file in the exercises directory to implement this exercise. To test it, run python test.py parse_row in your exercises directory.

Make a function parse_row that accepts a string consisting of two lines of comma-separated data and parses it, returning a dictionary where the keys are elements from the first row and the values are from the second row.

Example usage:

>>> from ranges import parse_row
>>> color_data = "purple,indigo,red,blue,green\n0.15,0.25,0.3,0.05,0.25"
>>> parse_row(color_data)
{'purple': '0.15', 'indigo': '0.25', 'red': '0.3', 'blue': '0.05', 'green': '0.25'}

with_previous

This is the with_previous exercise we’ve seen before in in ranges.py. It accepts a list and returns a new list that includes a tuple of each item and the previous item (the item just before it). The first “previous item” should be None. To test it, run python test.py with_previous in your exercises directory.

Re-write the code to use the zip function.

It should work like this:

>>> from ranges import with_previous
>>> numbers = [1, 2, 3, 4]
>>> with_previous(numbers)
[(1, None), (2, 1), (3, 2), (4, 3)]

Interleave

This is the interleave in ranges.py. The function accepts two iterables and returns a list with the items in each iterable “interleaved” (item 0 from iterable 1, then item 0 from iterable 2, then item 1 from iterable 1, and so on). Assume the two lists have the same length. To test it, run python test.py interleave in your exercises directory.

Re-write the code to use the zip function.

Example:

>>> from ranges import interleave
>>> interleave([1, 2, 3, 4], [5, 6, 7, 8])
[1, 5, 2, 6, 3, 7, 4, 8]
>>> interleave([1, 4, 7], [2, 5, 8])
[1, 2, 4, 5, 7, 8]