Say I have these three lists:
a = [1, 2, 3, 4]
b = [5, 6, 7, 8, 9]
c = [10, 11, 12]
Is there a builtin function such that:
Your output seems to be restricted by the output of the first iterator it1
. So we could use it1
as is, and pad it2
with infinite None
-yielding iterator, and zip
them.
>>> from itertools import repeat,izip,chain
>>> somezip = lambda it1,it2: izip(it1,chain(it2,repeat(None)))
>>> list(somezip(a,b))
[(1, 5), (2, 6), (3, 7), (4, 8)]
>>> list(somezip(a,c))
[(1, 10), (2, 11), (3, 12), (4, None)]
repeat(None) creates iterator yielding None
infinitely.
chain glues it2
and repeat(None)
.
izip
will stop yielding as soon as it1
is exhausted.
The other solutions have some flaws (I've left remarks in the comments). They may work well, but with some input they may fail unexpectedly.
As glglgl suggested in the comments, this function would rather accept variable number of iterators in parameters.
So I updated the code to work this:
from itertools import repeat,izip,chain,imap
somezip = lambda it1,*its: izip(it1,*imap(chain,its,repeat(repeat(None))))
Test:
>>> print(list(somezip(a,b)))
print(list(somezip(a,c)))
print(list(somezip(b,a,c)))
[(1, 5), (2, 6), (3, 7), (4, 8)]
[(1, 10), (2, 11), (3, 12), (4, None)]
[(5, 1, 10), (6, 2, 11), (7, 3, 12), (8, 4, None), (9, None, None)]
I had to use imap
here though there were no need to do so (as parameter are later unpacked, so plain map
would do). The reason was that map
don't accept iterators of different length, while imap
stop while the smallest iterator is consumed.
So, imap
applies chain
to all the iterators except for the first one and chain
s each of them with repeat(None)
. To serve every iterator of its
I used another repeat
above repeat(None)
(note, it may be very dangerous in the other projects, as all the objects which the outer repeat
produces are the same object repeat(None)
, so in the end they all the chain
ed iterators share it). Then I unpacked imap
object to produce the parameters to izip
, which returns the values until it1
is consumed (as chain
ed its
are now produce infinite sequence of values each).
Note that all the operations work in pure C, so there is no interpreter overhead involved.
To clarify how it works, I'm adding this elaboration:
def somezip(it1,*its): #from 0 to infinite iterators its
# it1 -> a1,a2,a3,...,an
# its -> (b1,b2,b3,...,bn),(c1,c2,c3,...,cn),...
infinite_None = repeat(None) # None,None,None,...
infinite_Nones = repeat(infinite_None) # infinite_None,infinite_None,... (share the same infinite_None)
chained = imap(chain,its,infinite_Nones) # [(b1,b2,b3,...,bn,None,None,...),(c1,c2,c3,...,cn,None,None,...),...]
return izip(it1,*chained)
And one-liner for it is just:
somezip = lambda it1,*its: izip(it1,*imap(chain,its,repeat(repeat(None))))