56

I have two lists

a = [1,2,3]
b = [9,10]

I want to combine (zip) these two lists into one list c such that

c = [(1,9), (2,10), (3, )]

Is there any function in standard library in Python to do this?

5 Answers 5

81

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,)]

Sign up to request clarification or add additional context in comments.

Comments

9

Another way is map:

a = [1, 2, 3]
b = [9, 10]
c = map(None, a, b)

Although that will too contain (3, None) instead of (3,). To do that, here's a fun line:

c = (tuple(y for y in x if y is not None) for x in map(None, a, b))

3 Comments

x if None not in x else tuple(y for y in x if y is not None). The x if None not in x is redundant here, as the else takes care of it. In the worst case, the else would return an empty tuple. Also, if there's a None in any tuple, the if would kill that tuple and pass it on to the else
@inspectorG4dget: Thanks. But now it's not quite as fun :D
Works nicely in Python 2.7, but not in 3.5. For 3.5 use itertools.zip_longest.
3

It's not too hard to just write the explicit Python to do the desired operation:

def izip_short(a, b):
    ia = iter(a)
    ib = iter(b)
    for x in ia:
        try:
            y = next(ib)
            yield (x, y)
        except StopIteration:
            yield (x,)
            break
    for x in ia:
        yield (x,)
    for y in ib:
        yield (None, y)

a = [1, 2, 3]
b = [9, 10]
list(izip_short(a, b))
list(izip_short(b, a))

I wasn't sure how you would want to handle the b sequence being longer than the a sequence, so I just stuff in a None for the first value in the tuple in that case.

Get an explicit iterator for each sequence. Run the a iterator as a for loop, while manually using next(ib) to get the next value from the b sequence. If we get a StopIteration on the b sequence, we break the loop and then for x in ia: gets the rest of the a sequence; after that for y in ib: will do nothing because that iterator is already exhausted. Alternatively, if the first for x in ia: loop exhausts the a iterator, the second for x in ia: does nothing but there could be values left in the b sequence and the for y in ib: loop collects them.

2 Comments

itertools.zip_longest can handle more than 2 arguments, unlike this code.
I don't remember what I was thinking in 2012 when I wrote this, but I think I probably just wanted to solve the problem. I have always felt that if there is a library function that does what you need, you almost never want to write your own instead of using it. Use the library function.
1

Single line:

c = zip(a, b) + [(x,) for x in a[len(b):]] + [(x,) for x in b[len(a):]]

If you want to reuse this:

def mergeUsNicely(a, b):
    def tupleMe(val):
        return (val,)
    return zip(a, b) + map(tupleMe, a[len(b):]) + map(tupleMe, b[len(a):])

Comments

0

This answer is an extension of the top answer that allows for arbitrary inputs instead of only two.

import itertools

sentinel = object()

def zip_longest_no_fill(*args):
    for i in itertools.zip_longest(*args, fillvalue=sentinel):
        yield tuple(x for x in i if x is not sentinel)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.