J'ai deux listes, dont la première est garantie de contenir exactement un élément de plus que le second . J'aimerais connaître la manière la plus pythonique de créer une nouvelle liste dont les valeurs d'index pair proviennent de la première liste et dont les valeurs d'index impairs proviennent de la deuxième liste.
# example inputs
list1 = ['f', 'o', 'o']
list2 = ['hello', 'world']
# desired output
['f', 'hello', 'o', 'world', 'o']
Cela fonctionne, mais ce n'est pas joli:
list3 = []
while True:
try:
list3.append(list1.pop(0))
list3.append(list2.pop(0))
except IndexError:
break
Comment peut-on y parvenir autrement? Quelle est l'approche la plus pythonique?
Réponses:
Voici une façon de le faire en découpant:
>>> list1 = ['f', 'o', 'o'] >>> list2 = ['hello', 'world'] >>> result = [None]*(len(list1)+len(list2)) >>> result[::2] = list1 >>> result[1::2] = list2 >>> result ['f', 'hello', 'o', 'world', 'o']
la source
Il y a une recette pour cela dans la
itertools
documentation :from itertools import cycle, islice def roundrobin(*iterables): "roundrobin('ABC', 'D', 'EF') --> A D E B F C" # Recipe credited to George Sakkis pending = len(iterables) nexts = cycle(iter(it).next for it in iterables) while pending: try: for next in nexts: yield next() except StopIteration: pending -= 1 nexts = cycle(islice(nexts, pending))
ÉDITER:
Pour la version de python supérieure à 3:
from itertools import cycle, islice def roundrobin(*iterables): "roundrobin('ABC', 'D', 'EF') --> A D E B F C" # Recipe credited to George Sakkis pending = len(iterables) nexts = cycle(iter(it).__next__ for it in iterables) while pending: try: for next in nexts: yield next() except StopIteration: pending -= 1 nexts = cycle(islice(nexts, pending))
la source
zip_longest
.itertools
documentation car.next()
ne fonctionne plus.__next__
. Ce n'est pas écrit dans la documentation, j'ai donc proposé une modification de la réponse.Cela devrait faire ce que vous voulez:
>>> iters = [iter(list1), iter(list2)] >>> print list(it.next() for it in itertools.cycle(iters)) ['f', 'hello', 'o', 'world', 'o']
la source
roundrobin
fonction est un peu exagérée pour cette situation.list(itertools.chain(map(next, itertools.cycle(iters)), *iters))
import itertools print [x for x in itertools.chain.from_iterable(itertools.izip_longest(list1,list2)) if x]
Je pense que c'est la manière la plus pythonique de le faire.
la source
None
s qui sont * supposés être dans la liste. Je vais éditer dans une version modifiée pour résoudre ce problèmeFalse
, ou même des choses qui ne seront évaluées queFalse
par l'if
expression -expression, comme par exemple une0
ou une liste vide. Cela peut être (partiellement) évité par ce qui suit:[x for x in itertools.chain.from_iterable(itertools.zip_longest(list1, list2)) if x is not None]
. Bien sûr, cela ne fonctionnera toujours pas si les listes contiennent desNone
éléments qui doivent être préservés. Dans ce cas, vous devez changer l'fillvalue
argument dezip_longest
, comme Dubslow l'a déjà suggéré.None
le problème semble avoir disparu, du moins depuis Python 3.7.6 (je ne sais pas pour les anciennes versions). Sialt_chain
est défini commedef alt_chain(*iters, fillvalue=None): return chain.from_iterable(zip_longest(*iters, fillvalue=fillvalue))
, alorslist(alt_chain([0, False, 1, set(), 3, 4], [0, None, 1, {}], fillvalue=99))
renvoie correctement[0, 0, False, None, 1, 1, set(), {}, 3, 99, 4, 99]
.Sans itertools et en supposant que l1 est 1 élément plus long que l2:
>>> sum(zip(l1, l2+[0]), ())[:-1] ('f', 'hello', 'o', 'world', 'o')
En utilisant itertools et en supposant que les listes ne contiennent aucun:
>>> filter(None, sum(itertools.izip_longest(l1, l2), ())) ('f', 'hello', 'o', 'world', 'o')
la source
[(l1[0], l2[0]), (l1[1], l2[1]), ...]
.sum
concatène les tuples ensemble:(l1[0], l2[0]) + (l1[1], l2[1]) + ...
résultant en des listes entrelacées. Le reste de la ligne est juste un rembourrage de l1 avec un élément supplémentaire pour que la fermeture éclair fonctionne et tranche jusqu'à -1 pour se débarrasser de ce rembourrage.filter(None, ...
(pourrait utiliser à labool
place, ouNone.__ne__
) supprime les fausses valeurs, y compris 0, None et les chaînes vides, donc la deuxième expression n'est pas strictement équivalente à la première.sum
cela? Quel est le rôle du deuxième argument ici? Dans les documentations, le deuxième argument eststart
.Je sais que les questions portent sur deux listes, l'une contenant un élément de plus que l'autre, mais j'ai pensé que je mettrais cela pour d'autres qui pourraient trouver cette question.
Voici la solution de Duncan adaptée pour fonctionner avec deux listes de tailles différentes.
list1 = ['f', 'o', 'o', 'b', 'a', 'r'] list2 = ['hello', 'world'] num = min(len(list1), len(list2)) result = [None]*(num*2) result[::2] = list1[:num] result[1::2] = list2[:num] result.extend(list1[num:]) result.extend(list2[num:]) result
Cela produit:
['f', 'hello', 'o', 'world', 'o', 'b', 'a', 'r']
la source
Si les deux listes ont la même longueur, vous pouvez faire:
[x for y in zip(list1, list2) for x in y]
Comme la première liste contient un élément supplémentaire, vous pouvez l'ajouter post hoc:
[x for y in zip(list1, list2) for x in y] + [list1[-1]]
la source
Voici une seule ligne qui le fait:
list3 = [ item for pair in zip(list1, list2 + [0]) for item in pair][:-1]
la source
def combine(list1, list2): lst = [] len1 = len(list1) len2 = len(list2) for index in range( max(len1, len2) ): if index+1 <= len1: lst += [list1[index]] if index+1 <= len2: lst += [list2[index]] return lst
la source
def combine(list1, list2, lst=[]):
, d'où mon commentaire. Au moment où j'ai soumis ce commentaire, cependant, killown avait apporté le changement nécessaire.Celui-ci est basé sur la contribution de Carlos Valiente ci-dessus avec une option pour alterner les groupes de plusieurs éléments et s'assurer que tous les éléments sont présents dans la sortie:
A=["a","b","c","d"] B=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16] def cyclemix(xs, ys, n=1): for p in range(0,int((len(ys)+len(xs))/n)): for g in range(0,min(len(ys),n)): yield ys[0] ys.append(ys.pop(0)) for g in range(0,min(len(xs),n)): yield xs[0] xs.append(xs.pop(0)) print [x for x in cyclemix(A, B, 3)]
Cela entrelacera les listes A et B par groupes de 3 valeurs chacun:
['a', 'b', 'c', 1, 2, 3, 'd', 'a', 'b', 4, 5, 6, 'c', 'd', 'a', 7, 8, 9, 'b', 'c', 'd', 10, 11, 12, 'a', 'b', 'c', 13, 14, 15]
la source
Il pourrait être un peu tard pour acheter un autre one-liner en python. Cela fonctionne lorsque les deux listes ont une taille égale ou inégale. Une chose qui ne vaut rien est que cela modifiera a et b. Si c'est un problème, vous devez utiliser d'autres solutions.
a = ['f', 'o', 'o'] b = ['hello', 'world'] sum([[a.pop(0), b.pop(0)] for i in range(min(len(a), len(b)))],[])+a+b ['f', 'hello', 'o', 'world', 'o']
la source
Ma prise:
a = "hlowrd" b = "el ol" def func(xs, ys): ys = iter(ys) for x in xs: yield x yield ys.next() print [x for x in func(a, b)]
la source
Voici une ligne unique utilisant des compréhensions de liste, sans autres bibliothèques:
list3 = [sub[i] for i in range(len(list2)) for sub in [list1, list2]] + [list1[-1]]
Voici une autre approche, si vous autorisez la modification de votre liste initiale1 par effet secondaire:
[list1.insert((i+1)*2-1, list2[i]) for i in range(len(list2))]
la source
from itertools import chain list(chain(*zip('abc', 'def'))) # Note: this only works for lists of equal length ['a', 'd', 'b', 'e', 'c', 'f']
la source
Arrêts sur le plus court:
def interlace(*iters, next = next) -> collections.Iterable: """ interlace(i1, i2, ..., in) -> ( i1-0, i2-0, ..., in-0, i1-1, i2-1, ..., in-1, . . . i1-n, i2-n, ..., in-n, ) """ return map(next, cycle([iter(x) for x in iters]))
Bien sûr, la résolution de la méthode next / __ next__ peut être plus rapide.
la source
Ceci est méchant mais fonctionne quelle que soit la taille des listes:
list3 = [element for element in list(itertools.chain.from_iterable([val for val in itertools.izip_longest(list1, list2)])) if element != None]
la source
Plusieurs one-liners inspirés des réponses à une autre question :
import itertools list(itertools.chain.from_iterable(itertools.izip_longest(list1, list2, fillvalue=object)))[:-1] [i for l in itertools.izip_longest(list1, list2, fillvalue=object) for i in l if i is not object] [item for sublist in map(None, list1, list2) for item in sublist][:-1]
la source
Que diriez-vous de numpy? Cela fonctionne également avec des chaînes:
import numpy as np np.array([[a,b] for a,b in zip([1,2,3],[2,3,4,5,6])]).ravel()
Résultat:
array([1, 2, 2, 3, 3, 4])
la source
Une alternative de manière fonctionnelle et immuable (Python 3):
from itertools import zip_longest from functools import reduce reduce(lambda lst, zipped: [*lst, *zipped] if zipped[1] != None else [*lst, zipped[0]], zip_longest(list1, list2),[])
la source
Je ferais le simple:
Il fournira un itérateur sans créer de besoins de stockage supplémentaires.
la source
chain.from_iterable(izip(list1, list2), list1[len(list2):])
pour le problème particulier posé ici ... list1 est censé être le plus long.Je suis trop vieux pour ne pas comprendre les listes, alors:
import operator list3 = reduce(operator.add, zip(list1, list2))
la source