4

I have a list of tuples that looks like this:

[('a', 'b'), ('c', 'd'), (('e', 'f'), ('h', 'i'))]

I want to turn it into this:

[('a', 'b'), ('c', 'd'), ('e', 'f'), ('h', 'i')]

What is the most Pythonic way to do this?

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
David Zhan Liu
  • 474
  • 5
  • 8

3 Answers3

6

one-line, using list comprehension:

l = [('a', 'b'), ('c', 'd'), (('e', 'f'), ('h', 'i'))]

result = [z for y in (x if isinstance(x[0],tuple) else [x] for x in l) for z in y]

print(result)

yields:

[('a', 'b'), ('c', 'd'), ('e', 'f'), ('h', 'i')]

this is artificially creating a list if the element is not a tuple of tuples, then flattening all does the job. To avoid creating a single element list [x], (x for _ in range(1)) can also do the job (although it appears clunky)

Limitation: doesn't handle more than 1 level of nesting. In which case, a more complex/recursive solution must be coded (check Martijn's answer).

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
5

Adjust the canonical un-flatten recipe to only unflatten when there are tuples in the value:

def flatten(l):
    for el in l:
        if isinstance(el, tuple) and any(isinstance(sub, tuple) for sub in el):
            for sub in flatten(el):
                yield sub
        else:
            yield el

This will only unwrap tuples, and only if there are other tuples in it:

>>> sample = [('a', 'b'), ('c', 'd'), (('e', 'f'), ('h', 'i'))]
>>> list(flatten(sample))
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('h', 'i')]
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
3

A one-line solution would be using itertools.chain:

>>> l = [('a', 'b'), ('c', 'd'), (('e', 'f'), ('h', 'i'))]
>>> from itertools import chain
>>> [*chain.from_iterable(x if isinstance(x[0], tuple) else [x] for x in l)]
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('h', 'i')]
L3viathan
  • 26,748
  • 2
  • 58
  • 81