web-dev-qa-db-fra.com

Entrelacement de plusieurs listes de même longueur en Python

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

Disons que je reçois [1,2,3] et [10,20,30]. Je voudrais transformer ceux-ci en [1,10,2,20,3,30].

58
NPE

Après avoir posté la question, je me suis rendu compte que je pouvais 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 Le meilleur moyen 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 d'égale longueur. 

81
NPE

Pour python> = 2.3, il existe une syntaxe extended slice :

>>> 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 d’entrelacement a et b: la première affecte les éléments de a à tous les index pairs de c; la seconde attribue les éléments de b à tous les index impairs de c.

45
ekhumoro

Donné

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

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]

Alternatives

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+

Bibliothèque livrée avec la recette roundrobin itertools, 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 dans Python 3 (bien que cela ne soit pas 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 avec pip install more_itertools

12
pylang

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. Par lui-même, avec None comme fonctions appelées, map produit une liste de n-uplets:

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

Il suffit ensuite d’aplatir la liste des n-uplets. 

L’avantage, bien sûr, est que map fonctionnera pour n’importe quel nombre de listes et fonctionnera même si leur longueur est différente:

>>> 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
the wolf

J'avais besoin d'un moyen de le faire avec des listes de tailles différentes auxquelles la réponse acceptée ne répond 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
Sandy Chapman

J'aime mieux la solution d'Aix. Voici une autre façon, je pense, devrait fonctionner dans la version 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 manière:

>>> 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
robert king
[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
jon_darkstar

Pour répondre au titre de la question "Entrelacer plusieurs listes de même longueur en Python", nous pouvons généraliser la réponse à 2 listes de @ekhumoro. Cela exige explicitement que les listes aient la même longueur, contrairement à la solution (élégante) de @NPE

import itertools

def interleave(lists):
    """Interleave a list of lists.

    :param lists: List of lists; each inner length must be the same length.
    :returns: interleaved single list
    :rtype: list

    """
    if len(set(len(_) for _ in lists)) > 1:
        raise ValueError("Lists are not all the same length!")
    joint = list(itertools.chain(*lists))
    for l_idx, li in enumerate(lists):
        joint[l_idx::len(lists)] = li
    return joint

Exemples:

>>> interleave([[0,2,4], [1, 3, 5]])
[0, 1, 2, 3, 4, 5]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12]])
[0, 1, 10, 2, 3, 11, 4, 5, 12]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14, 15]])
[0, 1, 10, 13, 2, 3, 11, 14, 4, 5, 12, 15]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14]])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 10, in interleave
ValueError: Lists are not all the same length!
>>> interleave([[0,2,4]])
[0, 2, 4]
0
eqzx