# inverse of izip

Peter Otten __peter__ at web.de
Thu Aug 19 10:46:20 CEST 2004

Steven Bethard wrote:

> Steven Bethard <steven.bethard <at> gmail.com> writes:
>> What's the inverse of izip?  Of course, I could use zip(*) or izip(*),
>> e.g.:
>>
>> >>> zip(*itertools.izip(range(10), range(10)))
>> [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)]
>> >>> x, y = itertools.izip(*itertools.izip(range(10), range(10)))
>> >>> x, y
>> ((0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
>>
>> But then I get a pair of tuples, not a pair of iterators.  Basically,
>> I want to convert an iterator of tuples into a tuple of iterators.
>
> Sorry to respond to myself, but after playing around with itertools for a
> while, this seems to work:
>
>>>> import itertools
>>>> starzip = lambda iterables: ((tuple[i] for tuple in itr) for i, itr in
> enumerate(itertools.tee(iterables)))
>>>> starzip(itertools.izip(range(10), range(10)))
> <generator object at 0x008DED28>
>>>> x, y = starzip(itertools.izip(range(10), range(10)))
>>>> x
> <generator object at 0x008E1058>
>>>> y
> <generator object at 0x008E1080>
>>>> list(x)
> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>> list(y)
> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>
> Seems like a bit of work for the inverse of izip though so I'll wait to
> see if
> anyone else has a better solution.  (Not to mention, it wouldn't be a
> single line solution if I wasn't using 2.4...)

Because Python supports function definitions you only have to do it once :-)

repeating your demo, you are getting the same (last) sequence twice due to
late binding of i.

>>> import itertools as it
>>> def starzip(iterables):
...     return ((t[i] for t in itr) for (i, itr) in
enumerate(it.tee(iterables)))
...
>>> map(list, starzip(it.izip("123", "abc")))
[['1', '2', '3'], ['a', 'b', 'c']]
>>> x, y = starzip(it.izip("123", "abc"))
>>> list(x)
['a', 'b', 'c']
>>> list(y)
['a', 'b', 'c']
>>>

Here's my fix.

# requires Python 2.4
def cut(itr, index):
# avoid late binding of index
return (item[index] for item in itr)

def starzip(tuples):
a, b = it.tee(tuples)
try:
tuple_len = len(a.next())
except StopIteration:
raise ValueError(
"starzip() does not allow an empty sequence as argument")
t = it.tee(b, tuple_len)
return (cut(itr, index) for (index, itr) in enumerate(t))

a, b, c = starzip(it.izip("abc", [1,2,3], "xyz"))
print a, b, c
assert list(a) == list("abc")
assert list(b) == [1, 2, 3]
assert list(c) == list("xyz")

Peter