views:

634

answers:

3

If I have sequence of sequences (maybe a list of tuples) I can use itertools.chain() to flatten it. But sometimes I feel like I would rather write it as a comprehension. I just can't figure out how to do it. Here's a very construed case:

Let's say I want to swap the elements of every pair in a sequence. I use a string as a sequence here:

>>> from itertools import chain
>>> seq = '012345'
>>> swapped_pairs = zip(seq[1::2], seq[::2])
>>> swapped_pairs
[('1', '0'), ('3', '2'), ('5', '4')]
>>> "".join(chain(*swapped_pairs))
'103254'

I use zip on the even and odd slices of the sequence to swap the pairs. But I end up with a list of tuples that now need to be flattened. So I use chain(). Is there a way I could express it with a comprehension instead?

If you want to post your own solution to the basic problem of swapping elements of the pairs, go ahead, I'll up-vote anything that teaches me something new. But I will only mark as accepted an answer that is targeted on my question, even if the answer is "No, you can't.".

A: 

You could use reduce to achive your goal:

In [6]: import operator
In [7]: a = [(1, 2), (2,3), (4,5)]
In [8]: reduce(operator.add, a, ())
Out[8]: (1, 2, 2, 3, 4, 5)

This return a tuple instead of a list because the elements in your original list are tuples that get concatenated. But you can easily build a list from that and the join method accepts tuples, too.

A list comprehension is, by the way, not the right tool for that. Basically a list comprehension builds a new list by describing how the elements of this list should look like. You want to reduce a list of elements to only one value.

unbeknown
-1: overhead of creating a new tuple each iteration would be too slow on a big list. also, reduce(operator.add, X, Y) is unreadable. Use sum(X, Y) instead.
nosklo
For me sum() has a too strong implication of an arithmetic operation. operator.add(), too, but thats the function we have to use for representing X + Y. So I think this is better to read than sum(). And speed wasn't in the requirements.
unbeknown
@heikogerlach: Do you really think reduce(operator.add, X, Y) is more readable than sum(X, Y)????? reduce is almost always unreadable. It has been removed from python builtins in python3.0 for that very reason. A for loop is almost always more readable.
nosklo
+5  A: 

With a comprehension? Well...

>>> seq = '012345'
>>> swapped_pairs = zip(seq[1::2], seq[::2])
>>> ''.join(item for pair in swapped_pairs for item in pair)
'103254'
nosklo
Thanks! This was driving me nuts. And now I'm a bit embarrassed. But I'll get over it. =)
PEZ
A: 
>>> a = [(1, 2), (3, 4), (5, 6)]
>>> reduce(tuple.__add__, a)
>>> (1, 2, 3, 4, 5, 6)

Or, to be agnostic about the type of inner sequences (as long as they are all the same):

>>> reduce(a[0].__class__.__add__, a)
scrible