Entrelacement de plusieurs listes en Python

En Python, existe-t-il un bon moyen d'entrelacer deux listes de même longueur?

Disons qu'on me donne [1,2,3] et [10,20,30]. J'aimerais les transformer en [1,10,2,20,3,30].

46
demandé sur coldspeed 2011-10-30 22:15:32

7 réponses

Après avoir posté la question, j'ai réalisé que je peux simplement faire ce qui suit:

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

l1 et l2 sont les deux listes.


S'il y a N listes à entrelacer, alors

lists = [l1, l2, ...]
[val for tup in zip(*lists) for val in tup]

Pour plus de recettes, suivez La meilleure façon d'entrelacer une liste avec ses valeurs de suffixe . Certaines des méthodes démontrées peuvent être généralisées à deux ou plusieurs listes de longueur égale.

73
répondu NPE 2018-06-12 17:04:11

Pour python> = 2.3, il y a syntaxe de tranche étendue :

>>> 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 ligne c = a + b est utilisée comme un moyen simple de créer une nouvelle liste de la longueur exacte (à ce stade, son contenu n'est pas important). Les deux lignes suivantes font le travail réel d'entrelacement a et b: la première assigne les éléments de a à tous les index pairs de c; la seconde assigne les éléments de b à tous les index impairs de c.

37
répondu ekhumoro 2017-11-04 17:09:31

Compte tenu de

a = [1, 2, 3]
b = [10, 20, 30]
c = [10, 20, 30, 99]

Code

En supposant des listes de longueur égale, vous pouvez obtenir une liste entrelacée avec itertools.chain et zip:

import itertools


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

Solutions de rechange

itertools.zip_longest

Plus généralement avec des listes inégales, utilisez zip_longest (recommandé):

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

De nombreuses listes peuvent être entrelacées en toute sécurité:

[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+

Une bibliothèque qui est livrée avec le roundrobin itertools recette, interleave et 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

Enfin, pour quelque chose d'intéressant en Python 3 (bien que non recommandé):

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]

+Installer via > pip install more_itertools

9
répondu pylang 2018-08-23 03:20:42

Alternative:

>>> l1=[1,2,3]
>>> l2=[10,20,30]
>>> [y for x in map(None,l1,l2) for y in x if y is not None]
[1, 10, 2, 20, 3, 30]

Cela fonctionne parce que map fonctionne sur les listes en parallèle. Il fonctionne de la même manière sous 2.2. En soi, avec None comme fonctions appelées, map produit une liste de tuples:

>>> map(None,l1,l2,'abcd')
[(1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c'), (None, None, 'd')]

Ensuite, il suffit d'aplatir la liste des tuples.

L'avantage, bien sûr, est que {[4] } fonctionnera pour n'importe quel nombre de listes et fonctionnera même si elles sont de longueurs différentes:

>>> l1=[1,2,3]
>>> l2=[10,20,30]
>>> l3=[101,102,103,104]
>>> [y for x in map(None,l1,l2,l3) for y in x if y in not None]
[1, 10, 101, 2, 20, 102, 3, 30, 103, 104]
6
répondu the wolf 2011-10-30 20:12:54

J'avais besoin d'un moyen de le faire avec des listes de tailles différentes que la réponse acceptée n'aborde pas.

Ma solution utilise un générateur et son utilisation semble un peu plus agréable à cause de cela:

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

Et son utilisation:

>>> 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']
6
répondu Sandy Chapman 2015-04-10 17:02:58

J'aime mieux la solution d'aix. voici une autre façon que je pense devrait fonctionner en 2.2:

>>> x=range(3)
>>> x
[0, 1, 2]
>>> y=range(7,10)
>>> y
[7, 8, 9]
>>> sum(zip(x,y),[])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "tuple") to list
>>> sum(map(list,zip(x,y)),[])
[0, 7, 1, 8, 2, 9]

Et une autre façon:

>>> a=[x,y]
>>> [a[i][j] for j in range(3) for i in (0,1)]
[0, 7, 1, 8, 2, 9]

Et:

>>> sum((list(i) for i in zip(x,y)),[])
[0, 7, 1, 8, 2, 9]
2
répondu robert king 2011-10-30 19:23:25
[el for el in itertools.chain(*itertools.izip_longest([1,2,3], [4,5])) if el is not None]

Tant Que vous n'avez pas None que vous voulez garder

0
répondu jon_darkstar 2016-12-21 00:02:43