¿Cómo puedo obtener un resultado plano de una lista de comprensión en lugar de una lista anidada?

5 minutos de lectura

Avatar de usuario de Steve Cooper
steve cooper

tengo una lista Ay una función f que toma un elemento de A y devuelve una lista. Puedo usar una lista de comprensión para convertir todo en A me gusta [f(a) for a in A], pero esto devuelve una lista de listas. Supongamos que mi entrada es [a1,a2,a3]Resultando en [[b11,b12],[b21,b22],[b31,b32]].

¿Cómo puedo obtener el aplanado lista [b11,b12,b21,b22,b31,b32] ¿en cambio? En otras palabras, en Python, ¿cómo puedo obtener lo que tradicionalmente se llama flatmap en lenguajes de programación funcionales, o SelectMany ¿en la red?

(En el código real, A es una lista de directorios, y f es os.listdir. Quiero construir una lista plana de subdirectorios.)


Ver también: ¿Cómo hago una lista plana a partir de una lista de listas? para el problema más general de aplanar una lista de listas después de haberla creado.

Avatar de usuario de Ants Aasma
Hormigas Asma

Puede tener iteraciones anidadas en una sola lista de comprensión:

[filename for path in dirs for filename in os.listdir(path)]

que es equivalente (al menos funcionalmente) a:

filenames = []
for path in dirs:
    for filename in os.listdir(path):
        filenames.append(filename)

  • Aunque inteligente, eso es difícil de entender y no muy legible.

    – Curtis Yallop

    20/10/2014 a las 23:01

  • Realmente no responde la pregunta como se le preguntó. Esto es más bien una solución para no encontrar el problema en primer lugar. ¿Qué pasa si ya tienes una lista de listas? Por ejemplo, ¿qué pasa si su lista de listas es el resultado de la función de mapa del módulo de multiprocesamiento? Quizás la solución de itertools o la solución de reducción sea la mejor.

    – Dave31415

    22 de enero de 2015 a las 3:43

  • Dave31415: [ item for list in listoflists for item in list ]

    – rampanion

    06/02/2015 a las 19:53

  • la ‘legibilidad’ es un juicio subjetivo. Encuentro esta solución bastante legible.

    – Cabaña Reb

    22 mayo 2015 a las 23:13

  • También pensé que era legible, hasta que vi el orden de los términos… 🙁

    – cz

    10 mayo 2017 a las 15:14

Avatar de usuario de Julian
Julian

>>> from functools import reduce  # not needed on Python 2
>>> list_of_lists = [[1, 2],[3, 4, 5], [6]]
>>> reduce(list.__add__, list_of_lists)
[1, 2, 3, 4, 5, 6]

los itertools La solución es más eficiente, pero esto se siente muy pitónico.

  • Para una lista de 1000 sublistas, esto es 100 veces más lento que el itertools manera y la diferencia solo empeora a medida que crece su lista.

    – Boris Verjovskiy

    7 de mayo a las 7:54


  • @BorisV: gracias por comparar eso. ¡No me sorprende! Esta versión probablemente esté bien para unas pocas docenas de sublistas, pero probablemente no debería usarse si el tamaño de entrada no tiene límites (o es grande).

    – Julian

    10 de mayo a las 14:17

avatar de usuario de rob
robar

Puedes encontrar una buena respuesta en el itertools recetas:

import itertools

def flatten(list_of_lists):
    return list(itertools.chain.from_iterable(list_of_lists))

  • Se puede usar el mismo enfoque para definir el mapa plano, como se propone en esta respuesta y esta entrada de blog externo

    – Josías Yoder

    11/07/2017 a las 19:53


Avatar de usuario de Wai Yip Tung
Wai Yip Tung

La pregunta propuesta flatmap. Se proponen algunas implementaciones pero pueden ser innecesarias creando listas intermedias. Aquí hay una implementación que se basa 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 de map.

Podrías simplemente hacer lo sencillo:

subs = []
for d in dirs:
    subs.extend(os.listdir(d))

  • Sí, está bien (aunque no tan bueno como el de @Ants), ¡así que le doy un +1 para honrar su simplicidad!

    – Alex Martelli

    3 de julio de 2009 a las 1:31

Avatar de usuario de la comunidad
Comunidad

Puede concatenar listas usando el operador de suma normal:

>>> [1, 2] + [3, 4]
[1, 2, 3, 4]

La función incorporada sum 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 puede definir su 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 para otra respuesta y supongo que es correcto que Python construya innecesariamente y recolecte basura 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 🙂

  • Sí, está bien (aunque no tan bueno como el de @Ants), ¡así que le doy un +1 para honrar su simplicidad!

    – Alex Martelli

    3 de julio de 2009 a las 1:31

subs = []
map(subs.extend, (os.listdir(d) for d in dirs))

(pero la respuesta de Ants es mejor; +1 para él)

  • Usar reduce (o sum, lo que le ahorra muchos caracteres y una importación 😉 para esto es simplemente incorrecto: sigue desechando inútilmente listas viejas para hacer una nueva para cada d. @Ants tiene la respuesta correcta (¡inteligencia de @Steve para aceptarla!).

    – Alex Martelli

    3 de julio de 2009 a las 1:28

  • No se puede decir en general que esta es una mala solución. Depende de si el rendimiento es incluso un problema. Simple es mejor a menos que haya una razón para optimizar. Es por eso que el método de reducción podría ser mejor para muchos problemas. Por ejemplo, tiene una función lenta que produce una lista de unos pocos cientos de objetos. Desea acelerarlo utilizando la función ‘mapa’ de multiprocesamiento. Entonces crea 4 procesos y usa reduce para mapearlos planamente. En este caso, la función de reducción está bien y es muy legible. Dicho esto, es bueno que señale por qué esto puede ser subóptimo. Pero no siempre es subóptimo.

    – Dave31415

    22 de enero de 2015 a las 3:48


¿Ha sido útil esta solución?