1

I have 2 lists of a different length

list_1 = [1, 2, 3, 4, 5]
list_2 = ['a', 'b']

If I'd do:

for (i,j) in itertools.zip_longest(list_1, list_2):
print (i,j)

The output would be

1 a
2 b
3 None
4 None
5 None

What I want is to iterate over those 2 lists, but repeat the shorter list when it gets exhausted any amount of times while the longer list still iterates. In this example I'd want output to be

1 a
2 b
3 a
4 b
5 a
smoke
  • 67
  • 1
  • 6
  • 1
    I was trying to find a duplicate, and was about to start writing an answer as I gave up... I got beaten by someone else looking for a duplicate *and* by someone writing an answer. :( – Karl Knechtel Jul 13 '22 at 01:17
  • Can `list_2` be longer than `list_1`? If so, what should be the result? – tdelaney Jul 13 '22 at 01:21

1 Answers1

6

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.

tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • 2
    You should probably wrap that with some simple logic checking which list is shortest rather than assuming it's always `list_2`. – Julien Jul 13 '22 at 01:18
  • 1
    @Julien - that could well be. I changed my answer assuming that OP wants to run through list_1 and only cycle list_2 as needed. But, if OP really does want the longest, you are absolutely right. – tdelaney Jul 13 '22 at 01:19
  • @Julien Easier to simply apply `cycle` to both, e.g `zip(map(cycle, (list_1, list_2)))` – blhsing Jul 13 '22 at 01:21
  • 4
    @blhsing Let us know when you're finished iterating through that. – Kelly Bundy Jul 13 '22 at 01:22
  • 1
    @blhsingv- that's an infinite loop. You'd have to choose which one to cycle. – tdelaney Jul 13 '22 at 01:22
  • Ah indeed lol. Wasn't thinking clearly. – blhsing Jul 13 '22 at 01:23
  • @blhsing But I guess it could be used to write a fast one for general iterables (which we can't ask for length). – Kelly Bundy Jul 13 '22 at 01:27
  • @KellyBundy - I think that the iterator would have to be resolved to a list first, otherwise we can't tell which to cycle. – tdelaney Jul 13 '22 at 01:33
  • @tdelaney No list, and I really do mean cycle **all** of them. And **none** of them :-) – Kelly Bundy Jul 13 '22 at 01:46
  • Frustratingly, I can't find a question asking for that. Everybody's just asking it for lists :-( – Kelly Bundy Jul 13 '22 at 01:49
  • @KellyBundy - I think I'd just implement it manually, do my own next's, cache in a list, and then adjust after StopIteration. Not going to commit to that though... – tdelaney Jul 13 '22 at 01:51
  • That would likely be relatively slow, though. – Kelly Bundy Jul 13 '22 at 01:55
  • Thanks for the answer! That's exactly what I need. I went over tens of postings before posting this and didn't find similar one, but now I see there are bunch of similar postings. – smoke Jul 13 '22 at 21:55