How do I make a flat list out of a list of lists?
Given a list of lists l
,
flat_list = [item for sublist in l for item in sublist]
which means:
flat_list = []
for sublist in l:
for item in sublist:
flat_list.append(item)
is faster than the shortcuts posted so far. (l
is the list to flatten.)
Here is the corresponding function:
def flatten(l):
return [item for sublist in l for item in sublist]
As evidence, you can use the timeit
module in the standard library:
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 143 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 1.1 msec per loop
Explanation: the shortcuts based on +
(including the implied use in sum
) are, of necessity, O(L**2)
when there are L sublists -- as the intermediate result list keeps getting longer, at each step a new intermediate result list object gets allocated, and all the items in the previous intermediate result must be copied over (as well as a few new ones added at the end). So, for simplicity and without actual loss of generality, say you have L sublists of I items each: the first I items are copied back and forth L-1 times, the second I items L-2 times, and so on; total number of copies is I times the sum of x for x from 1 to L excluded, i.e., I * (L**2)/2
.
The list comprehension just generates one list, once, and copies each item over (from its original place of residence to the result list) also exactly once.
Flatten an irregular list of lists
Using generator functions can make your example easier to read and improve performance.
Python 2
Using the Iterable
ABC added in 2.6:
from collections import Iterable
def flatten(xs):
for x in xs:
if isinstance(x, Iterable) and not isinstance(x, basestring):
for item in flatten(x):
yield item
else:
yield x
Python 3
In Python 3, basestring
is no more, but the tuple (str, bytes)
gives the same effect. Also, the yield from
operator returns an item from a generator one at a time.
from collections.abc import Iterable
def flatten(xs):
for x in xs:
if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
yield from flatten(x)
else:
yield x
How to get a flat list while avoiding to make a nested list in the first place?
You can use multiple for
s in a single comprehension:
result = [
n
for foo in list_of_foos
for n in foo.l
]
Note that the order of for
s is from the outside in -- same as if you wrote a nested for-loop:
for foo in list_of_foos:
for n in foo.l:
print(n)
Related Topics
How to Use 'Subprocess' Command With Pipes
Cannot Kill Python Script With Ctrl-C
How to Store the Result of an Executed Shell Command in a Variable in Python
How to Clone a List So That It Doesn't Change Unexpectedly After Assignment
Relative Imports For the Billionth Time
Why Is the Output of My Function Printing Out "None"
How to Type Hint a Method With the Type of the Enclosing Class
How to Add Value Labels on a Bar Chart
Why Does Concatenation of Dataframes Get Exponentially Slower
Imagemagick Not Authorized to Convert Pdf to an Image
Ensure a Single Instance of an Application in Linux
Call to Operating System to Open Url
How to Unnest (Explode) a Column in a Pandas Dataframe, into Multiple Rows
What Is Truthy and Falsy? How Is It Different from True and False
How to Parse Xml and Get Instances of a Particular Node Attribute
Is It Pythonic to Use List Comprehensions For Just Side Effects