How to zip two differently sized lists, repeating the shorter list?
You can use itertools.cycle
:
Make an iterator returning elements from the iterable and saving a copy of each. When the iterable is exhausted, return elements from the saved copy. Repeats indefinitely.
Example:
A = [1,2,3,4,5,6,7,8,9]
B = ["A","B","C"]
from itertools import cycle
zip_list = zip(A, cycle(B)) if len(A) > len(B) else zip(cycle(A), B)
How to zip lists of different sizes?
It's not pretty, but this is what I came up with:
In [10]: a = [1, 3, 5, 7, 9, 11]
...: b = [2, 4, 6, 8]
In [11]: output = (tuple(l[i] for l in (a,b) if i < len(l)) for i, e in enumerate(max(a,b, key=len)))
In [12]: list(output)
Out[12]: [(1, 2), (3, 4), (5, 6), (7, 8), (9,), (11,)]
Make it a function:
from collections.abc import (Iterable, Iterator)
from itertools import count
def zip_staggered(*args: Iterable) -> Iterator[tuple]:
for i in count():
if (next_elem := tuple(a[i] for a in args if i < len(a))):
yield next_elem
else:
break
Is there a zip-like function that pads to longest length?
In Python 3 you can use itertools.zip_longest
>>> list(itertools.zip_longest(a, b, c))
[('a1', 'b1', 'c1'), (None, 'b2', 'c2'), (None, 'b3', None)]
You can pad with a different value than None
by using the fillvalue
parameter:
>>> list(itertools.zip_longest(a, b, c, fillvalue='foo'))
[('a1', 'b1', 'c1'), ('foo', 'b2', 'c2'), ('foo', 'b3', 'foo')]
With Python 2 you can either use itertools.izip_longest
(Python 2.6+), or you can use map
with None
. It is a little known feature of map
(but map
changed in Python 3.x, so this only works in Python 2.x).
>>> map(None, a, b, c)
[('a1', 'b1', 'c1'), (None, 'b2', 'c2'), (None, 'b3', None)]
Zipping lists of unequal size
Normally, you use itertools.zip_longest
for this:
>>> import itertools
>>> a = [1, 2, 3]
>>> b = [9, 10]
>>> for i in itertools.zip_longest(a, b): print(i)
...
(1, 9)
(2, 10)
(3, None)
But zip_longest
pads the shorter iterable with None
s (or whatever value you pass as the fillvalue=
parameter). If that's not what you want then you can use a comprehension to filter out the None
s:
>>> for i in (tuple(p for p in pair if p is not None)
... for pair in itertools.zip_longest(a, b)):
... print(i)
...
(1, 9)
(2, 10)
(3,)
but note that if either of the iterables has None
values, this will filter them out too. If you don't want that, define your own object for fillvalue=
and filter that instead of None
:
sentinel = object()
def zip_longest_no_fill(a, b):
for i in itertools.zip_longest(a, b, fillvalue=sentinel):
yield tuple(x for x in i if x is not sentinel)
list(zip_longest_no_fill(a, b)) # [(1, 9), (2, 10), (3,)]
How to iterate over multiple lists of different lengths, but repeat the last value of a shorter list until the longest list is done?
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
Python - Iterate over 2 lists of a different length in a specific way
There's an itertools
for that. You want to cycle
the second list and do a vanilla zip
on the first. cycle
will remember and reemit values from list_2
and zip
will stop at the end of list_1
.
>>> import itertools
>>> list_1 = [1, 2, 3, 4, 5]
>>> list_2 = ['a', 'b']
>>> for i,j in zip(list_1, itertools.cycle(list_2)):
... print(i, j)
...
1 a
2 b
3 a
4 b
5 a
if you want the result to always be the longer of the two lists (either could cycle), you'd need to choose which one uses itertools.cycle
. There are a dozen ways to do that, but here's one
>>> zipper = zip(list_1, itertools.cycle(list_2)) if len(list_1) >= len(list_2) else zip(itertools.cycle(list_1), list_2)
>>> for i, j in zipper:
... print(i, j)
...
1 a
2 b
3 a
4 b
5 a
And if you want something that works for iterators in general (they wouldn't know their size in advance), you could make them into a list first.
Create a dictionary by zipping together two lists of uneven length
Use itertools.cycle
to cycle around to the beginning of L2
:
from itertools import cycle
dict(zip(L1, cycle(L2)))
# {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
In your case, concatenating L2
with itself also works.
# dict(zip(L1, L2 * 2))
dict(zip(L1, L2 + L2))
# {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
Loop over 2 lists, repeating the shortest until end of longest
Try
result = ["_".join((i, j)) for i, j in itertools.izip(la, itertools.cycle(lb))]
Related Topics
Performance of Pandas Apply VS Np.Vectorize to Create New Column from Existing Columns
How to Take a Screenshot/Image of a Website Using Python
Python/Ipython Importerror: No Module Named Site
Trying to Import a Module: Undefined Symbol: Pyunicodeucs4_Decodeutf8
How Do Rpy2, Pyrserve and Pyper Compare
How to Pass a List as a Command-Line Argument with Argparse
Display Image as Grayscale Using Matplotlib
Error: (-215) !Empty() in Function Detectmultiscale
How to Change the Figure Size of a Seaborn Axes or Figure Level Plot
Plotting a 3D Cube, a Sphere and a Vector in Matplotlib
Removing Duplicate Characters from a String
How to Drop a List of Rows from Pandas Dataframe
Converting JSON String to Dictionary Not List
Selenium Give File Name When Downloading
Django Auto_Now and Auto_Now_Add