Most programmatically elegant (pythonic) way to take ith element of list of lists of lists, and create lists of these ith elements

Viewed 146

Say that you have a list of lists, of lists, for example

lll = [(['a', 'b'], [1, 2], [True, False]),
       (['c'], [3], [True]),
       (['d', 'e', 'f', 'g'], [4, 5, 6, 7], [True, False, True, False])]

For each item in lll, I would like a list of the ith element in each of the items list, and I want all these lists in one list.

This is very tricky to describe with words as you can imagine, but I would like the final result to be like this

result = [
['a', 1, True],
['b', 2, False].
['c', 3, True],
['d', 4, True],
['e', 5, False],
['f', 6, True],
['g', 7, False]
]

What I tried so far is

newList = []
for item in lll:
    num_things = len(item[0])
    for ii in range(num_things):
        newList.append([x[ii] for x in item])

newList

Is there a more pythonically elegant way to get this result?

4 Answers

Your original code isn't bad at all. These existing answers are great, but I think

result = []

for item in lll:
    result.extend(zip(*item))

is reasonably succinct and Pythonic, arguably more readable than a one-liner, no imports and worth consideration. extend is a common refactor for append + loop for flattening lists and zip is almost always the way to go for columnwise iteration and matrix rotation.

If you need lists instead of tuples:

result = []

for item in lll:
    result.extend([list(x) for x in zip(*item)])

or with map:

for item in lll:
    result.extend(map(list, zip(*item)))

A couple of minor style suggestions courtesy of PEP-8:

  • snake_case is preferred over camelCase.
  • Avoid l as a variable name. It's too easily confused for i and 1.

Also, num_things = len(item[0]) strikes me as a bit dangerous because it'll raise an IndexError if item is empty.

Simple one-liner with no imports:

[[*ys] for xss in lll for ys in zip(*xss)]

The zip-star transposes, the second for concatenates, and the inner square brackets convert the tuples from the zip to lists.

Use zip + chain.from_iterable

from itertools import chain

o = chain.from_iterable(zip(*ll) for ll in lll)
print(list(o))

# output
# [('a', 1, True), ('b', 2, False), ('c', 3, True), ('d', 4, True), ('e', 5, False), ('f', 6, True), ('g', 7, False)]

You can then map each element to list using list comprehension asyou want list of lists and not list of tuples.

l = [list(i) for i in o]
print(l)

# output
# [['a', 1, True], ['b', 2, False], ['c', 3, True], ['d', 4, True], ['e', 5, False], ['f', 6, True], ['g', 7, False]]

I believe you want this:

list(map(list,zip(*[sum(l,[]) for l in zip(*lll)])))
Related