Como fazer uma lista simples de lista de listas
-
11-09-2019 - |
Pergunta
Gostaria de saber se existe um atalho para fazer uma lista simples de lista de listas em Python.
Eu posso fazer isso em um loop for
, mas talvez haja alguma cool "one-liner"? Eu tentei com reduzir , mas eu recebo um erro.
Código
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)
Mensagem de erro
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
Solução
Dada uma lista de listas l
,
flat_list = [item for sublist in l for item in sublist]
O que significa:
flat_list = []
for sublist in l:
for item in sublist:
flat_list.append(item)
é mais rápido do que os atalhos postado até agora. (l
a lista para achatar.)
Aqui é a função correspondente:
flatten = lambda l: [item for sublist in l for item in sublist]
Como prova, você pode usar o módulo timeit
na biblioteca padrão:
$ 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
Explicação: os atalhos com base em +
(incluindo a utilização implícita no sum
) são, necessariamente, O(L**2)
quando há L sublists - como a lista de resultado intermediário ficando cada vez mais, a cada passo um novo objeto lista resultado intermediário recebe alocado, e todos os itens no resultado intermediário anterior deve ser copiado (bem como alguns novos adicionados no final). Então, por simplicidade e sem perda real de generalidade, dizer que você tem L sublists de I itens cada: os primeiros itens I são copiados e para trás L-1 vezes, os itens de segunda I L-2 vezes, e assim por diante; número total de cópias é I vezes a soma de x para x a partir de um a L excluídos, isto é, I * (L**2)/2
.
A lista de compreensão apenas gera uma lista, uma vez, e cópias cada item sobre (a partir de seu local original de residência para a lista de resultados) também exatamente uma vez.
Outras dicas
Você pode usar itertools.chain()
:
>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain(*list2d))
ou, em Python> = 2,6, uso itertools.chain.from_iterable()
, que não requer a descompactação da lista:
>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain.from_iterable(list2d))
Esta abordagem é sem dúvida mais legível do que [item for sublist in l for item in sublist]
e parece ser mais rápido também:
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
10000 loops, best of 3: 24.2 usec per loop
[me@home]$ 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: 45.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 488 usec per loop
[me@home]$ 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: 522 usec per loop
[me@home]$ python --version
Python 2.7.3
Nota do autor : Este é ineficiente. Mas divertido, porque monoids são impressionantes. Não é apropriado para a produção de código Python.
>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Isso só resume os elementos da iterable passado no primeiro argumento, tratando segundo argumento como o valor inicial da soma (se não for dada, 0
é usado no lugar e este caso lhe dará um erro).
Porque você está soma listas aninhadas, você realmente obter [1,3]+[2,4]
como resultado de sum([[1,3],[2,4]],[])
, que é igual a [1,3,2,4]
.
Note que só funciona em listas de listas. Para listas de listas de listas, você vai precisar de uma outra solução.
I testados mais soluções com sugerida perfplot (um projeto de estimação da mina, essencialmente um invólucro em torno timeit
) , e encontrou
functools.reduce(operator.iconcat, a, [])
para ser a solução mais rápida. (operator.iadd
é igualmente rápido.)
Código para reproduzir o enredo:
import functools
import itertools
import numpy
import operator
import perfplot
def forfor(a):
return [item for sublist in a for item in sublist]
def sum_brackets(a):
return sum(a, [])
def functools_reduce(a):
return functools.reduce(operator.concat, a)
def functools_reduce_iconcat(a):
return functools.reduce(operator.iconcat, a, [])
def itertools_chain(a):
return list(itertools.chain.from_iterable(a))
def numpy_flat(a):
return list(numpy.array(a).flat)
def numpy_concatenate(a):
return list(numpy.concatenate(a))
perfplot.show(
setup=lambda n: [list(range(10))] * n,
kernels=[
forfor, sum_brackets, functools_reduce, functools_reduce_iconcat,
itertools_chain, numpy_flat, numpy_concatenate
],
n_range=[2**k for k in range(16)],
logx=True,
logy=True,
xlabel='num lists'
)
from functools import reduce #python 3
>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(lambda x,y: x+y,l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
O método extend()
no seu exemplo modifica x
vez de retornar um valor útil (que espera reduce()
).
A mais rápida maneira de fazer a versão reduce
seria
>>> import operator
>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Aqui está uma abordagem geral que se aplica a números , cordas , aninhada listas e mistos recipientes.
Código
#from typing import Iterable
from collections import Iterable # < py38
def flatten(items):
"""Yield items from any nested iterable; see Reference."""
for x in items:
if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
for sub_x in flatten(x):
yield sub_x
else:
yield x
Notas :
- Em Python 3,
yield from flatten(x)
pode substituirfor sub_x in flatten(x): yield sub_x
- Em Python 3.8, classes base abstratas mudou de
collection.abc
ao módulotyping
.
Demonstração
lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(flatten(lst)) # nested lists
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
mixed = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"] # numbers, strs, nested & mixed
list(flatten(mixed))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']
Referência
- Esta solução é modificada a partir de uma receita em Beazley, D. e B. Jones. Receita 4.14, Python Cookbook 3ª Ed, O'Reilly Media Inc. Sebastopol, CA:.. 2013
- SO anteriormente post, possivelmente, a demonstração originais.
Se você quer achatar a-estrutura de dados onde você não sabe quão profunda ela está aninhado você poderia usar iteration_utilities.deepflatten
1
>>> from iteration_utilities import deepflatten
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(deepflatten(l, depth=1))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l = [[1, 2, 3], [4, [5, 6]], 7, [8, 9]]
>>> list(deepflatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
É um gerador de modo que você precisa para converter o resultado para um list
ou explicitamente iterate sobre ele.
Para nivelar apenas um nível e se cada um dos itens é a própria iterable você também pode usar iteration_utilities.flatten
que em si é apenas um wrapper fino ao redor itertools.chain.from_iterable
:
>>> from iteration_utilities import flatten
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(flatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Apenas para adicionar alguns horários (baseado em Nico Schlömer resposta que não incluem a função apresentada nesta resposta):
É um gráfico log-log para acomodar a enorme gama de valores calibrados. Para raciocínio qualitativo: Menor é melhor
. Os resultados mostram que se o iterable contém apenas alguns iterables internas então sum
será mais rápido, no entanto por longos iterables apenas o itertools.chain.from_iterable
, iteration_utilities.deepflatten
ou a compreensão aninhada tem desempenho razoável com itertools.chain.from_iterable
sendo o mais rápido (como já foi observado por Nico Schlömer ).
from itertools import chain
from functools import reduce
from collections import Iterable # or from collections.abc import Iterable
import operator
from iteration_utilities import deepflatten
def nested_list_comprehension(lsts):
return [item for sublist in lsts for item in sublist]
def itertools_chain_from_iterable(lsts):
return list(chain.from_iterable(lsts))
def pythons_sum(lsts):
return sum(lsts, [])
def reduce_add(lsts):
return reduce(lambda x, y: x + y, lsts)
def pylangs_flatten(lsts):
return list(flatten(lsts))
def flatten(items):
"""Yield items from any nested iterable; see REF."""
for x in items:
if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
yield from flatten(x)
else:
yield x
def reduce_concat(lsts):
return reduce(operator.concat, lsts)
def iteration_utilities_deepflatten(lsts):
return list(deepflatten(lsts, depth=1))
from simple_benchmark import benchmark
b = benchmark(
[nested_list_comprehension, itertools_chain_from_iterable, pythons_sum, reduce_add,
pylangs_flatten, reduce_concat, iteration_utilities_deepflatten],
arguments={2**i: [[0]*5]*(2**i) for i in range(1, 13)},
argument_name='number of inner lists'
)
b.plot()
1 Disclaimer: Eu sou o autor dessa biblioteca
Eu levo minha declaração de volta. soma não é o vencedor. Embora seja mais rápido quando a lista é pequena. Mas os degrada significativamente o desempenho com listas maiores.
>>> timeit.Timer(
'[item for sublist in l for item in sublist]',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10000'
).timeit(100)
2.0440959930419922
A versão soma ainda está sendo executado por mais de um minuto e não tem feito o processamento ainda!
Para listas médio:
>>> timeit.Timer(
'[item for sublist in l for item in sublist]',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
).timeit()
20.126545906066895
>>> timeit.Timer(
'reduce(lambda x,y: x+y,l)',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
).timeit()
22.242258071899414
>>> timeit.Timer(
'sum(l, [])',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
).timeit()
16.449732065200806
Usando pequenas listas e timeit: number = 1000000
>>> timeit.Timer(
'[item for sublist in l for item in sublist]',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
).timeit()
2.4598159790039062
>>> timeit.Timer(
'reduce(lambda x,y: x+y,l)',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
).timeit()
1.5289170742034912
>>> timeit.Timer(
'sum(l, [])',
'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
).timeit()
1.0598428249359131
Parece haver uma confusão com operator.add
! Quando você adiciona duas listas juntos, o termo correto para isso é concat
, não adicionar. operator.concat
é o que você precisa para uso.
Se você está pensando funcional, é tão fácil como isso ::
>>> from functools import reduce
>>> list2d = ((1, 2, 3), (4, 5, 6), (7,), (8, 9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)
Você vê reduzir aspectos, o tipo de sequência, então quando você fornecer uma tupla, você recebe de volta uma tupla. Vamos tentar com uma lista ::
>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Aha, você recebe de volta uma lista.
Como sobre o desempenho ::
>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop
from_iterable
é bastante rápido! Mas há comparação para reduzir com concat
.
>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop
Por que você usa estender?
reduce(lambda x, y: x+y, l)
Este deve funcionar bem.
Considere a instalação do pacote more_itertools
.
> pip install more_itertools
Ele vem com uma implementação para flatten
( fonte , a partir do itertools receitas ):
import more_itertools
lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.flatten(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
A partir da versão 2.4, você pode aplainar mais complicado, iterables aninhadas com more_itertools.collapse
( fonte , contribuiu por abarnet).
lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9] # complex nesting
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
A razão a sua função não funcionou: a estender estende gama no local e não devolvê-lo. Você ainda pode voltar x de lambda, usando algum truque:
reduce(lambda x,y: x.extend(y) or x, l)
Nota:. Estender é mais eficiente do que + em listas
Não reinventar a roda se você estiver usando Django :
>>> from django.contrib.admin.utils import flatten
>>> l = [[1,2,3], [4,5], [6]]
>>> flatten(l)
>>> [1, 2, 3, 4, 5, 6]
... Pandas :
>>> from pandas.core.common import flatten
>>> list(flatten(l))
... itertools :
>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))
... Matplotlib
>>> from matplotlib.cbook import flatten
>>> list(flatten(l))
... Unipath :
>>> from unipath.path import flatten
>>> list(flatten(l))
... Setuptools :
>>> from setuptools.namespaces import flatten
>>> list(flatten(l))
def flatten(l, a):
for i in l:
if isinstance(i, list):
flatten(i, a)
else:
a.append(i)
return a
print(flatten([[[1, [1,1, [3, [4,5,]]]], 2, 3], [4, 5],6], []))
# [1, 1, 1, 3, 4, 5, 2, 3, 4, 5, 6]
Uma característica ruim da função de Anil acima é que ele requer que o usuário sempre especificar manualmente o segundo argumento de ser uma lista []
vazio. Isto deve preferivelmente ser um padrão. Devido à forma como Python objetos trabalho, estes devem ser fixados dentro da função, não nos argumentos.
Aqui está uma função de trabalho:
def list_flatten(l, a=None):
#check a
if a is None:
#initialize with empty list
a = []
for i in l:
if isinstance(i, list):
list_flatten(i, a)
else:
a.append(i)
return a
Testing:
In [2]: lst = [1, 2, [3], [[4]],[5,[6]]]
In [3]: lst
Out[3]: [1, 2, [3], [[4]], [5, [6]]]
In [11]: list_flatten(lst)
Out[11]: [1, 2, 3, 4, 5, 6]
matplotlib.cbook.flatten()
irá trabalhar para listas aninhadas, mesmo se eles ninho mais profundamente do que o exemplo.
import matplotlib
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
print(list(matplotlib.cbook.flatten(l)))
l2 = [[1, 2, 3], [4, 5, 6], [7], [8, [9, 10, [11, 12, [13]]]]]
print list(matplotlib.cbook.flatten(l2))
Resultado:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Esta é 18x mais rápido do que sublinhado ._ achatar:.
Average time over 1000 trials of matplotlib.cbook.flatten: 2.55e-05 sec
Average time over 1000 trials of underscore._.flatten: 4.63e-04 sec
(time for underscore._)/(time for matplotlib.cbook) = 18.1233394636
A resposta aceita não funcionou para mim quando se lida com listas baseadas em texto de comprimentos variáveis. Aqui é uma abordagem alternativa que fez o trabalho para mim.
l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]
aceitado resposta que fez não trabalho:
flat_list = [item for sublist in l for item in sublist]
print(flat_list)
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'xx', 'yyyyyyy']
New proposta solução que fez trabalho para mim:
flat_list = []
_ = [flat_list.extend(item) if isinstance(item, list) else flat_list.append(item) for item in l if item]
print(flat_list)
['aaa', 'bb', 'cccccc', 'xx', 'yyyyyyy']
versão recursiva
x = [1,2,[3,4],[5,[6,[7]]],8,9,[10]]
def flatten_list(k):
result = list()
for i in k:
if isinstance(i,list):
#The isinstance() function checks if the object (first argument) is an
#instance or subclass of classinfo class (second argument)
result.extend(flatten_list(i)) #Recursive call
else:
result.append(i)
return result
flatten_list(x)
#result = [1,2,3,4,5,6,7,8,9,10]
A seguir parece mais simples para mim:
>>> import numpy as np
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> print (np.concatenate(l))
[1 2 3 4 5 6 7 8 9]
Pode-se também usar plana do NumPy :
import numpy as np
list(np.array(l).flat)
Editar 2016/11/02:. Só funciona quando sublists tem dimensões idênticas
Você pode usar numpy:
flat_list = list(np.concatenate(list_of_list))
código simples para underscore.py
fã pacote
from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
Ele resolve todos os problemas achatar (item da lista nenhum ou nidificação complexo)
from underscore import _
# 1 is none list item
# [2, [3]] is complex nesting
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
Você pode instalar underscore.py
com pip
pip install underscore.py
def flatten(alist):
if alist == []:
return []
elif type(alist) is not list:
return [alist]
else:
return flatten(alist[0]) + flatten(alist[1:])
flat_list = []
for i in list_of_list:
flat_list+=i
Este código também funciona bem como ele só ampliar a lista todo o caminho. Embora seja muito semelhante, mas só tem um loop for. Por isso, têm menos complexidade do que adicionar 2 para loops.
Se você está disposto a desistir de uma pequena quantidade de velocidade para uma aparência mais limpa, então você poderia usar numpy.concatenate().tolist()
ou numpy.concatenate().ravel().tolist()
:
import numpy
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99
%timeit numpy.concatenate(l).ravel().tolist()
1000 loops, best of 3: 313 µs per loop
%timeit numpy.concatenate(l).tolist()
1000 loops, best of 3: 312 µs per loop
%timeit [item for sublist in l for item in sublist]
1000 loops, best of 3: 31.5 µs per loop
Você pode descobrir mais aqui nos docs numpy.concatenate e numpy.ravel
solução mais rápida que eu encontrei (para uma lista grande de qualquer maneira):
import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()
Feito! Pode, claro, transformá-lo de volta para uma lista de lista de execução (l)
Esta pode não ser a forma mais eficiente, mas eu pensei que colocar um one-liner (na verdade, um dois-liner). Ambas as versões irá funcionar nas listas hierarquia aninhada arbitrárias, e explora recursos de linguagem (Python3.5) e recursão.
def make_list_flat (l):
flist = []
flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
return flist
a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)
A saída é
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
Isso funciona em uma primeira maneira profundidade. A recursão vai para baixo até encontrar um elemento não-lista, em seguida, estende o flist
variável local e, em seguida, reverte-lo para o pai. Sempre que flist
é devolvido, ele é estendido para flist
do pai na lista de compreensão. Portanto, na raiz, uma lista simples é retornado.
O de cima cria várias listas e devolve-os locais que são usados ??para estender a lista do pai. Acho que a volta para isso pode ser a criação de um flist
gloabl, como abaixo.
a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]
make_list_flat(a)
print (flist)
A saída é novamente
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
Embora eu não estou certo neste momento sobre a eficiência.
Nota ??strong>: A seguir aplica-se a Python 3.3+ porque usa yield_from
. six
é também um pacote de terceiros, embora seja estável. Como alternativa, você poderia usar sys.version
.
No caso de obj = [[1, 2,], [3, 4], [5, 6]]
, todas as soluções aqui são bons, incluindo lista de compreensão e itertools.chain.from_iterable
.
No entanto, considerar este caso um pouco mais complexo:
>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]
Há vários problemas aqui:
- Um elemento,
6
, é apenas um escalar; não é iterable, por isso as rotas acima falhará aqui. - Um elemento,
'abc'
, é tecnicamente iterable (todosstr
s são). No entanto, lendo nas entrelinhas um pouco, você não quer tratá-la como tal - você quer tratá-lo como um único elemento .
- O elemento final,
[8, [9, 10]]
é em si um iterable aninhada. compreensão lista básica echain.from_iterable
única extrair "1 nível baixo".
Você pode remediar esta situação da seguinte forma:
>>> from collections import Iterable
>>> from six import string_types
>>> def flatten(obj):
... for i in obj:
... if isinstance(i, Iterable) and not isinstance(i, string_types):
... yield from flatten(i)
... else:
... yield i
>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]
Aqui, você verificar que o sub-elemento (1) é iterable com Iterable
, um ABC de itertools
, mas também quer garantir que (2) o elemento é não "string-like."
Outra abordagem incomum que funciona para hetero e listas homogêneos de números inteiros:
from typing import List
def flatten(l: list) -> List[int]:
"""Flatten an arbitrary deep nested list of lists of integers.
Examples:
>>> flatten([1, 2, [1, [10]]])
[1, 2, 1, 10]
Args:
l: Union[l, Union[int, List[int]]
Returns:
Flatted list of integer
"""
return [int(i.strip('[ ]')) for i in str(l).split(',')]
Um método recursivo simples usando reduce
de functools
eo operador add
em listas:
>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
A função flatten
leva em lst
como parâmetro. Ele faz um loop todos os elementos de lst
até inteiros atingindo (também pode alterar int
para float
, str
, etc. para outros tipos de dados), que são adicionados ao valor de retorno da recursão mais externa.
A recursão, ao contrário de métodos como laços for
e mônadas, é que é uma solução geral não limitados pela profundidade da lista ??strong>. Por exemplo, uma lista com profundidade de 5 pode ser achatada da mesma forma que l
:
>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]