Concatenation of inner lists or ints
Question:
I feel like I’m missing something obvious, but there it is… I would like to go from:
lst = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
to:
output = [0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, 2]
I can do this with a for loop such as:
output = []
for l in lst:
if hasattr(l, '__iter__'):
output.extend(l)
else:
output.append(l)
Maybe the for-loop is fine, but it feels like there should be a more elegant way to do this… Trying to do this with numpy seems even more convoluted because ragged arrays aren’t easily handled… so you can’t (for example):
output = np.asanyarray(lst).flatten().tolist()
Thanks in advance.
Update:
Here’s my comparison between the two methods provided by @T.J and @Ashwini – thanks to both!
In [5]: %paste
from itertools import chain
from collections import Iterable
lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
def solve(lis):
for x in lis:
if isinstance(x, Iterable) and not isinstance(x, basestring):
yield x
else:
yield [x]
%timeit list(chain.from_iterable(solve(lis)))
%timeit [a for x in lis for a in (x if isinstance(x, Iterable) and not isinstance(x,basestring) else [x])]
## -- End pasted text --
100000 loops, best of 3: 10.1 us per loop
100000 loops, best of 3: 8.12 us per loop
Update2:
...
lis = lis *10**5
%timeit list(chain.from_iterable(solve(lis)))
%timeit [a for x in lis for a in (x if isinstance(x, Iterable) and not isinstance(x,basestring) else [x])]
## -- End pasted text --
1 loops, best of 3: 699 ms per loop
1 loops, best of 3: 698 ms per loop
Answers:
You can use itertools.chain
like this:
>>> from itertools import chain
>>> from collections import Iterable
>>> lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
def solve(lis):
for x in lis:
if isinstance(x, Iterable) and not isinstance(x, basestring):
yield x
else:
yield [x]
...
>>> list(chain.from_iterable(solve(lis)))
[0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, 2]
Works fine for strings too:
>>> lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], "234"]
>>> list(chain.from_iterable(solve(lis)))
[0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, '234']
Timing comparisons:
>>> lis = lis *(10**4)
#modified version of FJ's answer that works for strings as well
>>> %timeit [a for x in lis for a in (x if isinstance(x, Iterable) and not isinstance(x,basestring) else [x])]
10 loops, best of 3: 110 ms per loop
>>> %timeit list(chain.from_iterable(solve(lis)))
1 loops, best of 3: 98.3 ms per loop
You can Use generator to make list consist of iterables only
((i if isinstance(i,Iterable) else [i])
And then use any of following methods to concatenate them into one list:
would You try sum
from collections import Iterable
lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
sum(((i if isinstance(i,Iterable) else [i]) for i in lis), [])
You should provide initial value for sum []
to start sum from
or itertools.chain()
but in this case You should unpack Your upper-level list into set of lists
import itertools
lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
list(itertools.chain(*((i if isinstance(i,Iterable) else [i]) for i in lis)))
or just list comprehension
[a for x in input for a in (x if isinstance(x, Iterable) else [x])]
But One should mind that strings are iterable as well. And If there will be string on upper level it would be spited into chars.
Here is a pretty straightforward approach that uses a list comprehension:
>>> data = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
>>> [a for x in data for a in (x if isinstance(x, list) else [x])]
[0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, 2]
Here are timing comparisons, it looks like my version is slightly faster (note that I modified my code to use collections.Iterable
as well to make sure the comparison is fair):
In [9]: %timeit list(chain.from_iterable(solve(data)))
100000 loops, best of 3: 9.22 us per loop
In [10]: %timeit [a for x in data for a in (x if isinstance(x, Iterable) else [x])]
100000 loops, best of 3: 6.45 us per loop
I feel like I’m missing something obvious, but there it is… I would like to go from:
lst = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
to:
output = [0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, 2]
I can do this with a for loop such as:
output = []
for l in lst:
if hasattr(l, '__iter__'):
output.extend(l)
else:
output.append(l)
Maybe the for-loop is fine, but it feels like there should be a more elegant way to do this… Trying to do this with numpy seems even more convoluted because ragged arrays aren’t easily handled… so you can’t (for example):
output = np.asanyarray(lst).flatten().tolist()
Thanks in advance.
Update:
Here’s my comparison between the two methods provided by @T.J and @Ashwini – thanks to both!
In [5]: %paste
from itertools import chain
from collections import Iterable
lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
def solve(lis):
for x in lis:
if isinstance(x, Iterable) and not isinstance(x, basestring):
yield x
else:
yield [x]
%timeit list(chain.from_iterable(solve(lis)))
%timeit [a for x in lis for a in (x if isinstance(x, Iterable) and not isinstance(x,basestring) else [x])]
## -- End pasted text --
100000 loops, best of 3: 10.1 us per loop
100000 loops, best of 3: 8.12 us per loop
Update2:
...
lis = lis *10**5
%timeit list(chain.from_iterable(solve(lis)))
%timeit [a for x in lis for a in (x if isinstance(x, Iterable) and not isinstance(x,basestring) else [x])]
## -- End pasted text --
1 loops, best of 3: 699 ms per loop
1 loops, best of 3: 698 ms per loop
You can use itertools.chain
like this:
>>> from itertools import chain
>>> from collections import Iterable
>>> lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
def solve(lis):
for x in lis:
if isinstance(x, Iterable) and not isinstance(x, basestring):
yield x
else:
yield [x]
...
>>> list(chain.from_iterable(solve(lis)))
[0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, 2]
Works fine for strings too:
>>> lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], "234"]
>>> list(chain.from_iterable(solve(lis)))
[0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, '234']
Timing comparisons:
>>> lis = lis *(10**4)
#modified version of FJ's answer that works for strings as well
>>> %timeit [a for x in lis for a in (x if isinstance(x, Iterable) and not isinstance(x,basestring) else [x])]
10 loops, best of 3: 110 ms per loop
>>> %timeit list(chain.from_iterable(solve(lis)))
1 loops, best of 3: 98.3 ms per loop
You can Use generator to make list consist of iterables only
((i if isinstance(i,Iterable) else [i])
And then use any of following methods to concatenate them into one list:
would You try sum
from collections import Iterable
lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
sum(((i if isinstance(i,Iterable) else [i]) for i in lis), [])
You should provide initial value for sum []
to start sum from
or itertools.chain()
but in this case You should unpack Your upper-level list into set of lists
import itertools
lis = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
list(itertools.chain(*((i if isinstance(i,Iterable) else [i]) for i in lis)))
or just list comprehension
[a for x in input for a in (x if isinstance(x, Iterable) else [x])]
But One should mind that strings are iterable as well. And If there will be string on upper level it would be spited into chars.
Here is a pretty straightforward approach that uses a list comprehension:
>>> data = [[0, 1, 3, 7, 8, 11, 12], [8, 0, 1, 2, 3, 14], 2]
>>> [a for x in data for a in (x if isinstance(x, list) else [x])]
[0, 1, 3, 7, 8, 11, 12, 8, 0, 1, 2, 3, 14, 2]
Here are timing comparisons, it looks like my version is slightly faster (note that I modified my code to use collections.Iterable
as well to make sure the comparison is fair):
In [9]: %timeit list(chain.from_iterable(solve(data)))
100000 loops, best of 3: 9.22 us per loop
In [10]: %timeit [a for x in data for a in (x if isinstance(x, Iterable) else [x])]
100000 loops, best of 3: 6.45 us per loop