davidchambers
tengo dos listas el primero de los cuales está garantizado que contiene exactamente un elemento más que el segundo. Me gustaría conocer la forma más pitónica de crear una nueva lista cuyos valores de índice par provienen de la primera lista y cuyos valores de índice impar provienen de la segunda lista.
# example inputs
list1 = ['f', 'o', 'o']
list2 = ['hello', 'world']
# desired output
['f', 'hello', 'o', 'world', 'o']
Esto funciona, pero no es bonito:
list3 = []
while True:
try:
list3.append(list1.pop(0))
list3.append(list2.pop(0))
except IndexError:
break
¿De qué otra manera se puede lograr esto? ¿Cuál es el enfoque más pitónico?
Si necesita manejar listas de longitud no coincidente (por ejemplo, la segunda lista es más larga, o la primera tiene más de un elemento más que la segunda), algunas soluciones aquí funcionarán mientras que otras requerirán ajustes. Para obtener respuestas más específicas, consulte ¿Cómo intercalar dos listas de diferente longitud? dejar el exceso de elementos al final, o ¿Cómo intercalar elegantemente dos listas de longitud desigual en python? para tratar de intercalar los elementos de manera uniforme.
Aquí hay una forma de hacerlo cortando:
>>> 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']
-
Gracias, Duncan. No me di cuenta de que es posible especificar un paso al cortar. Lo que me gusta de este enfoque es la naturalidad con la que se lee. 1. Haz una lista de la longitud correcta. 2. Rellenó los índices pares con el contenido de list1. 3. Rellene los índices impares con el contenido de list2. ¡El hecho de que las listas tengan diferentes longitudes no es un problema en este caso!
– davidchambers
9 de septiembre de 2010 a las 17:39
-
Creo que solo funciona cuando len(list1) – len(list2) es 0 o 1.
– xan
9 de septiembre de 2010 a las 19:01
-
Si las listas tienen la longitud adecuada, entonces funciona; de lo contrario, la pregunta original no especifica qué respuesta se espera. Se puede modificar fácilmente para manejar las situaciones más razonables: por ejemplo, si desea que se ignoren los elementos adicionales, simplemente elimine la lista más larga antes de comenzar; si desea que los elementos adicionales se intercalen con Ninguno, simplemente asegúrese de que el resultado se inicialice con más Ninguno; si desea agregar elementos adicionales al final, haga lo mismo que para ignorarlos y luego agréguelos.
– Duncan
9 de septiembre de 2010 a las 19:58
-
Yo tampoco estaba claro. El punto que estaba tratando de señalar es que la solución de Duncan, a diferencia de muchas de las enumeradas, no se complica por el hecho de que las listas tienen una longitud desigual. Claro, es aplicable solo en un rango limitado de situaciones, pero preferiría una solución realmente elegante que funcione en este caso a una solución menos elegante que funcione para dos listas cualesquiera.
– davidchambers
9 de septiembre de 2010 a las 23:48
-
Puedes usar (2*len(lista1)-1) en lugar de (len(lista1)+len(lista2)), también prefiero [0::2] en vez de [::2].
– Señor Británico
10 de septiembre de 2010 a las 0:38
david z
Hay una receta para esto en el itertools
documentación (nota: para Python 3):
from itertools import cycle, islice
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
num_active = len(iterables)
nexts = cycle(iter(it).__next__ for it in iterables)
while num_active:
try:
for next in nexts:
yield next()
except StopIteration:
# Remove the iterator we just exhausted from the cycle.
num_active -= 1
nexts = cycle(islice(nexts, num_active))
-
Encuentro esta manera más complicada de lo que debe ser. Hay una mejor opción a continuación usando
zip_longest
.– Dubslow
16 de noviembre de 2017 a las 12:39
-
@Dubslow Para este caso particular, sí, esto probablemente sea excesivo (como mencioné en un comentario en otro lugar), a menos que ya tenga acceso a él. Sin embargo, podría tener algunas ventajas en otras situaciones. Esta receta ciertamente no fue diseñada para este problema, simplemente lo resuelve.
– David Z.
16 de noviembre de 2017 a las 19:17
-
Para tu información, deberías usar la receta en el
itertools
documentación porque.next()
ya no funciona– Juan W.
2 de junio de 2020 a las 15:21
-
@johnw. uno tiene que usar
__next__
. No está escrito en la documentación, así que propuse una edición de la respuesta.– Marine Galantin
5 de agosto de 2020 a las 12:53
-
@Marine Preferiría que hubieras cambiado el ejemplo de código existente, pero puedo arreglarlo yo mismo. ¡Gracias por contribuir!
– David Z.
5 de agosto de 2020 a las 17:53
vamsi nerella
import itertools
print [x for x in itertools.chain.from_iterable(itertools.izip_longest(list1,list2)) if x]
Creo que esta es la forma más pitónica de hacerlo.
-
¿Por qué esta no es la respuesta aceptada? ¡Este es el más corto y más pitónico y funciona con diferentes longitudes de lista!
–Jairo Vadillo
19 de agosto de 2016 a las 8:36
-
el nombre del método es zip_longest no izip_longest
–Jairo Vadillo
19 de agosto de 2016 a las 8:37
-
El problema con esto es que el valor de relleno predeterminado de zip_longest podría sobrescribir
None
s que *se supone que están en la lista. Editaré en una versión modificada para arreglar esto– Dubslow
16 de noviembre de 2017 a las 12:27
-
Nota: Esto causará problemas si las listas contienen elementos con valor
False
o incluso cosas que solo serán evaluado comoFalse
por elif
-expresión, como por ejemplo a0
o una lista vacía. Esto se puede evitar (parcialmente) con lo siguiente:[x for x in itertools.chain.from_iterable(itertools.zip_longest(list1, list2)) if x is not None]
. Por supuesto, esto seguirá sin funcionar si las listas contienenNone
elementos que necesitan ser preservados. En este caso, debe cambiar elfillvalue
argumento dezip_longest
como ya sugirió Dubslow.– der_herr_g
14 mayo 2019 a las 16:35
-
None
el problema parece haber desaparecido, al menos desde Python 3.7.6 (no sé para versiones anteriores). Sialt_chain
Se define comodef alt_chain(*iters, fillvalue=None): return chain.from_iterable(zip_longest(*iters, fillvalue=fillvalue))
despuéslist(alt_chain([0, False, 1, set(), 3, 4], [0, None, 1, {}], fillvalue=99))
devuelve correctamente[0, 0, False, None, 1, 1, set(), {}, 3, 99, 4, 99]
.– paime
23 de julio de 2020 a las 9:49
marca byers
En Python 2, esto debería hacer lo que quieras:
>>> iters = [iter(list1), iter(list2)]
>>> print list(it.next() for it in itertools.cycle(iters))
['f', 'hello', 'o', 'world', 'o']
Zart
Sin itertools y suponiendo que l1 es 1 elemento más largo que l2:
>>> sum(zip(l1, l2+[0]), ())[:-1]
('f', 'hello', 'o', 'world', 'o')
En python 2, usando itertools y asumiendo que las listas no contienen Ninguno:
>>> filter(None, sum(itertools.izip_longest(l1, l2), ()))
('f', 'hello', 'o', 'world', 'o')
-
Esta es mi respuesta favorita. Es tan conciso.
– bombomb007
18 mayo 2017 a las 16:25
-
@anishtain4 zip toma pares de elementos como tuplas de listas,
[(l1[0], l2[0]), (l1[1], l2[1]), ...]
.sum
concatena tuplas juntas:(l1[0], l2[0]) + (l1[1], l2[1]) + ...
resultando en listas intercaladas. El resto de la línea única es solo el relleno de l1 con un elemento adicional para que zip funcione y corte hasta -1 para deshacerse de ese relleno.– Zart
14 de mayo de 2020 a las 5:08
-
izip_longest (zip_longest desde python 3) no necesita +[0] relleno, implícitamente llena Ninguno cuando las longitudes de las listas no coinciden, mientras que
filter(None, ...
(podría utilizarbool
en cambio, oNone.__ne__
) elimina los valores falsos, incluidos 0, Ninguno y cadenas vacías, por lo que la segunda expresión no es estrictamente equivalente a la primera.– Zart
14 de mayo de 2020 a las 5:14
-
la pregunta es como hiciste
sum
¿Haz eso? ¿Cuál es el papel del segundo argumento allí? En las documentaciones, el segundo argumento esstart
.– anishtain4
14 mayo 2020 a las 14:20
-
El valor predeterminado de inicio es 0, y no puede hacer 0+ (algo, tupla), por lo tanto, el inicio se cambia a una tupla vacía.
– Zart
14 mayo 2020 a las 16:15
Si ambas listas tienen la misma longitud, puede hacer:
[x for y in zip(list1, list2) for x in y]
Como la primera lista tiene un elemento más, puede agregarlo post hoc:
[x for y in zip(list1, list2) for x in y] + [list1[-1]]
-
Esta es mi respuesta favorita. Es tan conciso.
– bombomb007
18 mayo 2017 a las 16:25
-
@anishtain4 zip toma pares de elementos como tuplas de listas,
[(l1[0], l2[0]), (l1[1], l2[1]), ...]
.sum
concatena tuplas juntas:(l1[0], l2[0]) + (l1[1], l2[1]) + ...
resultando en listas intercaladas. El resto de la línea única es solo el relleno de l1 con un elemento adicional para que zip funcione y corte hasta -1 para deshacerse de ese relleno.– Zart
14 de mayo de 2020 a las 5:08
-
izip_longest (zip_longest desde python 3) no necesita +[0] relleno, implícitamente llena Ninguno cuando las longitudes de las listas no coinciden, mientras que
filter(None, ...
(podría utilizarbool
en cambio, oNone.__ne__
) elimina los valores falsos, incluidos 0, Ninguno y cadenas vacías, por lo que la segunda expresión no es estrictamente equivalente a la primera.– Zart
14 de mayo de 2020 a las 5:14
-
la pregunta es como hiciste
sum
¿Haz eso? ¿Cuál es el papel del segundo argumento allí? En las documentaciones, el segundo argumento esstart
.– anishtain4
14 mayo 2020 a las 14:20
-
El valor predeterminado de inicio es 0, y no puede hacer 0+ (algo, tupla), por lo tanto, el inicio se cambia a una tupla vacía.
– Zart
14 mayo 2020 a las 16:15
Comunidad
Sé que las preguntas se refieren a dos listas, una de las cuales tiene un elemento más que el otro, pero pensé que pondría esto para otros que puedan encontrar esta pregunta.
Aquí está la solución de Duncan adaptada para trabajar con dos listas de diferentes tamaños.
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
Esto da como resultado:
['f', 'hello', 'o', 'world', 'o', 'b', 'a', 'r']
@Paul: Sí, la respuesta aceptada no brinda la solución completa. Lea los comentarios y las otras respuestas. La pregunta es básicamente la misma y las otras soluciones se pueden aplicar aquí.
– Félix Kling
9 de septiembre de 2010 a las 17:23
@Felix: respetuosamente no estoy de acuerdo. Es cierto, las preguntas están en el mismo vecindario pero en realidad no son duplicadas. Como prueba vaga, eche un vistazo a las posibles respuestas aquí y compárelas con la otra pregunta.
-Paul Sasik
9 de septiembre de 2010 a las 17:28
Echa un vistazo a estos: stackoverflow.com/questions/7529376/…
– palabras para el sabio
9 de agosto de 2016 a las 22:22