functional programming - comprensiones de la lista de pitones; ¿Comprimir una lista de listas?
python sum (13)
Google me trajo la siguiente solución:
def flatten(l):
if isinstance(l,list):
return sum(map(flatten,l))
else:
return l
chicos. Intento encontrar la solución más elegante para un problema y me pregunto si Python tiene algo incorporado para lo que estoy tratando de hacer.
Lo que estoy haciendo es esto. Tengo una lista, A
, y tengo una función f
que toma un elemento y devuelve una lista. Puedo usar una lista de comprensión para convertir todo en A
así;
[f(a) for a in A]
Pero esto devuelve una lista de listas;
[a1,a2,a3] => [[b11,b12],[b21,b22],[b31,b32]]
Lo que realmente quiero es obtener la lista aplanada;
[b11,b12,b21,b22,b31,b32]
Ahora, otros lenguajes lo tienen; tradicionalmente se denomina flatmap
en los lenguajes de programación funcionales, y .Net lo llama SelectMany
. ¿Python tiene algo similar? ¿Hay una forma ordenada de asignar una función sobre una lista y aplanar el resultado?
El problema real que estoy tratando de resolver es esto; comenzando con una lista de directorios, encuentre todos los subdirectorios. asi que;
import os
dirs = ["c://usr", "c://temp"]
subs = [os.listdir(d) for d in dirs]
print subs
currentliy me da una lista de listas, pero realmente quiero una lista.
La pregunta propuesta flatmap
. Se proponen algunas implementaciones pero pueden crear listas intermedias innecesarias. Aquí hay una implementación basada en iteradores.
def flatmap(func, *iterable):
return itertools.chain.from_iterable(map(func, *iterable))
In [148]: list(flatmap(os.listdir, [''c:/mfg'',''c:/Intel'']))
Out[148]: [''SPEC.pdf'', ''W7ADD64EN006.cdr'', ''W7ADD64EN006.pdf'', ''ExtremeGraphics'', ''Logs'']
En Python 2.x, use itertools.map
en lugar del map
.
Puede concatenar listas usando el operador de suma normal:
>>> [1, 2] + [3, 4]
[1, 2, 3, 4]
La sum
función incorporada agregará los números en una secuencia y opcionalmente puede comenzar desde un valor específico:
>>> sum(xrange(10), 100)
145
Combine lo anterior para aplanar una lista de listas:
>>> sum([[1, 2], [3, 4]], [])
[1, 2, 3, 4]
Ahora puedes definir tu flatmap
:
>>> def flatmap(f, seq):
... return sum([f(s) for s in seq], [])
...
>>> flatmap(range, [1,2,3])
[0, 0, 1, 0, 1, 2]
Editar: Acabo de ver la crítica en los comentarios en busca de otra respuesta y creo que es correcto que Python compilará innecesariamente y que la basura recolecte muchas listas más pequeñas con esta solución. Entonces, lo mejor que se puede decir al respecto es que es muy simple y conciso si estás acostumbrado a la programación funcional :-)
Puede encontrar una buena respuesta en las recetas de itertools:
def flatten(listOfLists):
return list(chain.from_iterable(listOfLists))
(Nota: requiere Python 2.6+)
Puede tener iteraciones anidadas en una sola lista de comprensión:
[filename for path in dirs for filename in os.listdir(path)]
Puedes probar itertools.chain()
, así:
import itertools
import os
dirs = ["c://usr", "c://temp"]
subs = list(itertools.chain(*[os.listdir(d) for d in dirs]))
print subs
itertools.chain()
devuelve un iterador, de ahí el pasar a list()
.
Puedes usar pyxtension :
from pyxtension.streams import stream
stream([ [1,2,3], [4,5], [], [6] ]).flatMap() == range(7)
Usted podría simplemente hacer lo siguiente:
subs = []
for d in dirs:
subs.extend(os.listdir(d))
>>> listOfLists = [[1, 2],[3, 4, 5], [6]]
>>> reduce(list.__add__, listOfLists)
[1, 2, 3, 4, 5, 6]
Supongo que la solución itertools es más eficiente que esto, pero se siente muy pitónico y evita tener que importar una biblioteca solo por el simple hecho de una operación de lista.
If listA=[list1,list2,list3]
flattened_list=reduce(lambda x,y:x+y,listA)
Esto lo hara.
def flat_list(arr):
send_back = []
for i in arr:
if type(i) == list:
send_back += flat_list(i)
else:
send_back.append(i)
return send_back
import itertools
x=[[''b11'',''b12''],[''b21'',''b22''],[''b31'']]
y=list(itertools.chain(*x))
print y
itertools funcionará desde python2.3 y superior
subs = []
map(subs.extend, (os.listdir(d) for d in dirs))
(pero la respuesta de las hormigas es mejor, +1 para él)