How to iterate over multiple lists of different lengths, but repeat the last value of a shorter list until the longest list is done?

Viewed 1341

In my Python 3 script, I am trying to make a combination of three numbers from three different lists based on inputs. If the lists are the same size, there is no issue with zip. However, I want to be able to input a single number for a specific list and the script to repeat that number until the longest list is finished. This can be done with zip_longest. However, with fillvalue it is not possible to have separate fill values for separate lists.

Taking this simple script as an example:

from itertools import zip_longest

list1=[1]
list2=[4, 5, 6, 7, 8, 9]
list3=[2]
for l1, l2, l3 in zip_longest(list1, list2, list3):
     print(l1, l2, l3)

This is the actual result:

# 1    4 2
# None 5 None                                                        
# None 6 None                                                         
# None 7 None
# None 8 None
# None 9 None  

And this would be the result that I want:

# 1 4 2
# 1 5 2                                                        
# 1 6 2                                                         
# 1 7 2
# 1 8 2
# 1 9 2                                                        
 

I already managed to do this specific task by manually creating different for loops and asking if a list is a constant or not, but zip_longest is so close to exactly what I need that I wonder if I am missing something obvious.

3 Answers

You could make use of logical or operator to use the last element of the shorter lists:

from itertools import zip_longest
list1 = [1]
list2 = ["a", "b", "c", "d", "e", "f"]
list3 = [2]
for l1, l2, l3 in zip_longest(list1, list2, list3):
    print(l1 or list1[-1], l2, l3 or list3[-1])

Out:

1 a 2
1 b 2
1 c 2
1 d 2
1 e 2
1 f 2

You can make use of itertools.cycle, which takes a list and returns a generator, looping through the contents of the list without stop.

from itertools import cycle


list1 = [1]
list2 = [4, 5, 6, 7, 8, 9]
list3 = [2]
for l1, l2, l3 in zip(cycle(list1), list2, cycle(list3)):
     print(l1, l2, l3)

Output:

1 4 2
1 5 2
1 6 2
1 7 2
1 8 2
1 9 2

Note that we used the regular zip() instead of zip_longest(), otherwise cycle(list1) and cycle(list3) would keep generating values and we would encounter an infinite loop.

If you just have one number you'd like to repeat, you can use repeat(x) instead.

from itertools import repeat


x, y = 1, 2
list_ = [4, 5, 6, 7, 8, 9]

for l1, l2, l3 in zip(repeat(x), list_, repeat(y)):
     print(l1, l2, l3)

The unique point with cycle is that your lists will be repeated. For example, the following set of lists will generate a different output from Meyer's solution:

list1 = [1, 3]
list2 = [4, 5, 6, 7, 8, 9]
list3 = [2]

Output:

1 4 2
3 5 2
1 6 2
3 7 2
1 8 2
3 9 2
zipped = zip(list1 * len(list2), list2, list3 * len(list2))

for item in zipped:
    print(item)  

(1, 'a', 2)
(1, 'b', 2)
(1, 'c', 2)
(1, 'd', 2)
(1, 'e', 2)
(1, 'f', 2)
Related