Intercalar varias listas de la misma longitud en Python [duplicado]

Resuelto NPE asked hace 12 años • 11 respuestas

En Python, ¿existe una buena manera de intercalar dos listas de la misma longitud?

Di que me dan [1,2,3]y [10,20,30]. Me gustaría transformarlos en [1,10,2,20,3,30].

NPE avatar Oct 31 '11 01:10 NPE
Aceptado

Después de publicar la pregunta, me di cuenta de que simplemente puedo hacer lo siguiente:

[val for pair in zip(l1, l2) for val in pair]

donde l1y l2están las dos listas.


Si hay N listas para intercalar, entonces

lists = [l1, l2, ...]
[val for tup in zip(*lists) for val in tup]
NPE avatar Oct 30 '2011 18:10 NPE

Para Python>=2.3, hay una sintaxis de segmento extendida :

>>> a = [0, 2, 4, 6, 8]
>>> b = [1, 3, 5, 7, 9]
>>> c = a + b
>>> c[::2] = a
>>> c[1::2] = b
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

La línea c = a + bse utiliza como una forma sencilla de crear una nueva lista de exactamente la longitud correcta (en esta etapa, su contenido no es importante). Las siguientes dos líneas hacen el trabajo real de entrelazado ay b: la primera asigna los elementos de aa todos los índices pares de c; el segundo asigna los elementos de ba todos los índices impares de c.

ekhumoro avatar Oct 30 '2011 20:10 ekhumoro

Dado

a = [1, 2, 3]
b = [10, 20, 30]
c = [100, 200, 300, 999]

Código

Suponiendo listas de igual longitud, puede obtener una lista intercalada con itertools.chainy zip:

import itertools


list(itertools.chain(*zip(a, b)))
# [1, 10, 2, 20, 3, 30]

Alternativas

itertools.zip_longest

De manera más general, con listas desiguales, use zip_longest(recomendado):

[x for x in itertools.chain(*itertools.zip_longest(a, c)) if x is not None]
# [1, 100, 2, 200, 3, 300, 999]

Se pueden intercalar muchas listas de forma segura:

[x for x in itertools.chain(*itertools.zip_longest(a, b, c)) if x is not None]
# [1, 10, 100, 2, 20, 200, 3, 30, 300, 999]

more_itertools+

Una biblioteca que se envía con la roundrobinreceta itertools interleavey interleave_longest.

import more_itertools


list(more_itertools.roundrobin(a, b))
# [1, 10, 2, 20, 3, 30]

list(more_itertools.interleave(a, b))
# [1, 10, 2, 20, 3, 30]

list(more_itertools.interleave_longest(a, c))
# [1, 100, 2, 200, 3, 300, 999]

yield from

Finalmente, algo interesante en Python 3 (aunque no recomendado):

list(filter(None, ((yield from x) for x in zip(a, b))))
# [1, 10, 2, 20, 3, 30]

list([(yield from x) for x in zip(a, b)])
# [1, 10, 2, 20, 3, 30]

+ Instalar usandopip install more_itertools

pylang avatar Dec 04 '2016 00:12 pylang

Necesitaba una forma de hacer esto con listas de diferentes tamaños que la respuesta aceptada no aborda.

Mi solución utiliza un generador y su uso se ve un poco mejor gracias a ello:

def interleave(l1, l2):
    iter1 = iter(l1)
    iter2 = iter(l2)
    while True:
        try:
            if iter1 is not None:
                yield next(iter1)
        except StopIteration:
            iter1 = None
        try:
            if iter2 is not None:
                yield next(iter2)
        except StopIteration:
            iter2 = None
        if iter1 is None and iter2 is None:
            raise StopIteration()

Y su uso:

>>> a = [1, 2, 3, 4, 5]
>>> b = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> list(interleave(a, b))
[1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 'e', 'f', 'g']
>>> list(interleave(b, a))
['a', 1, 'b', 2, 'c', 3, 'd', 4, 'e', 5, 'f', 'g']
Sandy Chapman avatar Apr 10 '2015 17:04 Sandy Chapman