How to Zip Two Differently Sized Lists, Repeating the Shorter List

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 Nones (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 Nones:

>>> 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



Leave a reply



Submit