Is there an intermediate point between `zip` and` zip_longest`

Let's say I have three lists:

a = [1, 2, 3, 4] b = [5, 6, 7, 8, 9] c = [10, 11, 12] 

Is there a built-in function such that:

 somezip(a, b) == [(1, 5), (2, 6), (3, 7), (4, 8)] somezip(a, c) == [(1, 10), (2, 11), (3, 12), (4, None)] 

Keeping somewhere between zip and zip_longest ?

+7
source share
5 answers

No, no, but you can easily combine the takewhile and izip_longest functionality to achieve the desired

 from itertools import takewhile, izip_longest from operator import itemgetter somezip = lambda *p: list(takewhile(itemgetter(0),izip_longest(*p))) 

(In case the first iterator may have elements that are evaluated as False, you can replace itemgetter with a lambda expression - see @ovgolovin comment)

 somezip = lambda *p: list(takewhile(lambda e: not e[0] is None,izip_longest(*p))) 

Examples

 >>> from itertools import takewhile, izip_longest >>> from operator import itemgetter >>> a = [1, 2, 3, 4] >>> b = [5, 6, 7, 8, 9] >>> c = [10, 11, 12] >>> somezip(a,b) [(1, 5), (2, 6), (3, 7), (4, 8)] >>> somezip(a,c) [(1, 10), (2, 11), (3, 12), (4, None)] >>> somezip(b,c) [(5, 10), (6, 11), (7, 12), (8, None), (9, None)] 
+8
source
 import itertools as it somezip = lambda *x: it.islice(it.izip_longest(*x), len(x[0])) >>> list(somezip(a,b)) [(1, 5), (2, 6), (3, 7), (4, 8)] >>> list(somezip(a,c)) [(1, 10), (2, 11), (3, 12), (4, None)] 
+5
source

It seems your output is limited by the output of the first it1 iterator. Thus, we could use it1 as is, and pad it2 with an infinite None reinforcing 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 an iterator inferior to None infinitely.

chain gluing it2 and repeat(None) .

izip will stop working as soon as it1 is exhausted.

Other solutions have some drawbacks (I left comments in the comments). They may work well, but with some input they may unexpectedly fail.


As the glglgl suggested in the comments, this function is more likely to accept a variable number of iterators in the parameters.

So, I updated the code to work:

 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, although there was no need to do this (since the parameter will be unpacked later, the usual map will be executed). The reason was that map did not accept iterators of different lengths , and imap stopped while consuming the smallest iterator.

So imap applies chain to all iterators except the first one, and chain each of them with repeat(None) . To serve each of its iterators, I used another repeat above repeat(None) (note that this can be very dangerous in other projects, since all objects that the external repeat produces are the same repeat(None) object, therefore, after all, all the teters of it chain share it). Then I unpacked the imap object to get the izip parameters, which returns values ​​until it1 is consumed (since chain ed its now produce an infinite sequence of values ​​each).

Please note that all operations are performed in pure C, so there is no need to use the interpreter overhead.

To clarify how this works, I add this development:

 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-line for him is simple:

 somezip = lambda it1,*its: izip(it1,*imap(chain,its,repeat(repeat(None)))) 
+3
source

define your own function:

 In [64]: def myzip(*args): lenn=len(args[0]) return list(izip_longest(*[islice(x,lenn) for x in args],fillvalue=None)) ....: In [30]: myzip(a,b) Out[30]: [(1, 5), (2, 6), (3, 7), (4, 8)] In [31]: myzip(b,c) Out[31]: [(5, 10), (6, 11), (7, 12), (8, None), (9, None)] In [32]: myzip(a,c) Out[32]: [(1, 10), (2, 11), (3, 12), (4, None)] 
0
source

It is more than others, but relatively easy to understand if it matters .; -)

 a = [1, 2, 3, 4] b = [5, 6, 7, 8, 9] c = [10, 11, 12] def g(n): return xrange(n) # simple generator def my_iter(iterable, fillvalue=None): for i in iterable: yield i while True: yield fillvalue def somezip(*iterables, **kwds): fillvalue = kwds.get('fillvalue') iters = [my_iter(i, fillvalue) for i in iterables] return [tuple(next(it) for it in iters) for i in iterables[0]] print 'somezip(a, b):', somezip(a, b) print 'somezip(a, c):', somezip(a, c) print 'somezip(a, g(2)):', somezip(a, g(2)) print 'somezip(g(2), a):', somezip(g(2),a) print 'somezip(a, b, c):', somezip(a, b, c) print 'somezip(a, b, c, g(2)):', somezip(a, b, c, g(2)) print 'somezip(g(2), a, b, c):', somezip(g(2), a, b, c) 

Output:

 somezip(a, b): [(1, 5), (2, 6), (3, 7), (4, 8)] somezip(a, c): [(1, 10), (2, 11), (3, 12), (4, None)] somezip(a, g(2)): [(1, 0), (2, 1), (3, None), (4, None)] somezip(g(2), a): [(1, 1)] somezip(a, b, c): [(1, 5, 10), (2, 6, 11), (3, 7, 12), (4, 8, None)] somezip(a, b, c, g(2)): [(1, 5, 10, 0), (2, 6, 11, 1), (3, 7, 12, None), (4, 8, None, None)] somezip(g(2), a, b, c): [(1, 1, 5, 10)] 
0
source

All Articles