Cómo Recorrer Dos Listas a la Vez en Python

Cómo Recorrer Dos Listas a la Vez en Python

A veces sucede que no tenemos bien agregada determinada información que está relacionada entre sí. Por ejemplo, en una lista tenemos los nombres de los estudiantes de una clase y en otra lista sus calificaciones. Ambas listas están ordenadas, de manera que al primer estudiante le corresponde la primera calificación, al segundo estudiante, la segunda calificación, etc. ¿Cómo hacemos para recorrer las dos listas simultáneamente de manera cómoda y sencilla? Te lo cuento…

Una manera sencilla de recorrer dos listas a la vez en Python es utilizar la función zip. Dadas las listas l1 y l2 basta con hacer el bucle for v1, v2 in zip(l1, l2) y en cada vuelta de bucle se obtendrá sucesivamente un valor de cada lista en las variables v1 y v2, ambos de la misma posición.

Esta manera de iterar por dos listas a la vez, es decir, de recorrer en paralelo dos listas, no es la única y pueden existir situaciones en las que se prefiere otra forma de hacerlo. Te cuento a continuación otras posibles maneras para que puedas aplicar la que más te conviene.

Recorrer dos listas a la vez con zip

En Python, la función zip devuelve un iterador de tuplas donde cada tupla contiene los valores de la misma posición de dos o más elementos iterables, como las listas.

El nombre de la función resultante bastante visual pues zip, en inglés, es cremallera. Basta con pensar en las dos partes de una cremallera en la que sus dientes se van entrelazando uno a uno según se va cerrando.

Volviendo a nuestro campo, lo vemos con un ejemplo. Imagina que tenemos listas, una lista a con los valores [1, 2, 3] y otra lista b con los valores [4, 5, 6]. Al llamar a la función zip pasándole como argumentos estas dos listas obtendremos un iterador con los valores [(1, 4), (2, 5), (3, 6)].

Si te fijas, en la primera posición tenemos una tupla que combina los valores de la primera posición de cada una de las listas a y b. En la segunda posición tenemos otra tupla con los valores de la segunda posición de las listas. Y en la tercera… bueno, ya lo sabes.

Ahora podemos usar ese iterador haciendo un único recorrido para acceder a los valores de ambas listas. Te presento dos versiones similares, una primera en la que accedemos directamente a cada tupla del iterador y otra en la que accedemos directamente a los valores:

lista_a = [1, 2, 3]
lista_b = [4, 5, 6]


for tupla in zip(lista_a, lista_b): #obtenemos la tupla en cada iteración
	print(tupla[0], tupla[1])

for valor_a, valor_b in zip(lista_a, lista_b): #obtenemos los valores en cada iteración
	print(valor_a, valor_b)

Si ejecutamos el código anterior, cualquiera de esos dos bucles va a generar el mismo resultado por pantalla, que es el siguiente:

1 4
2 5
3 6

Como ves, es bastante cómodo.

Si por lo que sea necesitas guardar la lista de tuplas tal cual nos las ofrece el iterador, basta que llames a la función list de esta manera: combinada = list(zip(lista_a, lista_b)). Si mostramos por pantalla el contenido de la lista combinada obtendremos lo siguiente:

[(1, 4), (2, 5), (3, 6)]

¡Cuidado! La función zip se detiene en cuanto una de las dos listas se acaba. Esto hay que tenerlo en cuenta si manejamos listas de diferentes tamaños.

Recorrer dos listas a la vez con enumerate

Si necesitamos un poco más de control sobre la iteración podemos utilizar la función enumerate.

En Python, la función enumerate devuelve un iterador de tuplas con dos valores: un contador, que puede servir de índice de la colección que estamos recorriendo, y un dato de dicha colección. Así, en el mismo bucle se recorre la lista obteniendo índice y su dato correspondiente.

La verdad es que esta función resulta muy cómoda y nos evita tener que definir contadores o variables de índices (y tener que controlarlos, claro). Vemos un ejemplo para que lo entiendas mejor y veas su uso y utilidad:

lista = ['Código', 'Pitón', 'www.codigopiton.com']

for indice, valor in enumerate(lista):
	print(f'El valor de la posición {indice} es {valor}')

Al ejecutar el código anterior obtenemos el siguiente resultado:

El valor de la posición 0 es Código
El valor de la posición 1 es Pitón
El valor de la posición 2 es www.codigopiton.com

Pero claro, dirás que este ejemplo solo hay una lista y se trata de recorrer dos listas a la vez, ¿o no? Pues ahí esta el truco. Ahora tenemos en una variable indice el índice o posición del elemento actual en cada vuelta del bucle. Podemos usar ese índice para acceder al valor de la otra lista:

lista_a = [1, 2, 3]
lista_b = [4, 5, 6]

for indice, valor_a in enumerate(lista_a):
	print(valor_a, lista_b[indice])

Y obtendremos como resultado:

1 4
2 5
3 6

Esta opción nos da un poco más de flexibilidad porque nos permite hacer uso del índice para lo que necesitemos. Por ejemplo, podríamos querer modificar los valores de la lista según los vamos recorriendo, y para eso necesitamos el índice, ya que los valores que obtenemos a través del zip o enumerate son copias (si no se trata de objetos) de los valores de las listas.

Recorrer dos listas a la vez por índice

Si queremos prescindir de las funciones presentadas en este artículo y hacer el recorrido de una manera más clásica (y menos al estilo de Python) podemos hacerlo. Te presento dos alternativas.

La primera opción es utilizar un bucle for que recorra todos los números entre 0 y la longitud de las listas. Para ello nos apoyaremos de la función len, que nos da la longitud de las listas y la función range (te aclaro en otro artículo el funcionamiento de la función range).

lista_a = [1, 2, 3]
lista_b = [4, 5, 6]

for indice in range(0, len(lista_a)):
	print(lista_a[indice], lista_b[indice])

De esta manera, como ya he dicho, en cada vuelta del bucle tenemos cada uno de los índices de las listas entre 0 y la longitud de las mismas (sin incluir este último valor). Ya podemos usar ese índice para acceder a los datos correspondientes.

La segunda opción, que es la más manual, pero también la más flexible, consiste en el uso de un bucle while y en controlar la variable indice para hacer que tome, precisamente, los valores de posición de las listas. Lo hacemos de la siguiente manera:

indice = 0 # 0 es la primera posición

while indice < len(lista_a): # repetimos mientras no hayamos llegado al final de las listas
	print(lista_a[indice], lista_b[indice])
	indice += 1 #incrementamos el índice en una unidad

Decía que estas dos formas resultan más flexibles que las anteriores (bueno, tal vez también podemos incluir la opción con enumerate) porque al contar con el valor del índice nos permite hacer que los recorridos no sean exactamente en paralelo, es decir, elemento a elemento y en el mismo orden.

Por ejemplo, podríamos querer recorrer una lista al derecho y otra al revés o cualquier otra variante que se te ocurra, como por ejemplo, una lista de elemento en elemento y otra de dos en dos elementos. Si contamos con el valor de los índices, este tipo de recorridos resultan mucho más cómodos.

¡Ojo! Ten cuidado nuevamente con los límites de las listas y con el manejo y recorrido de listas de diferentes tamaños. Puede ayudarte el uso de la función min para calcular la longitud mínima de las listas: longitud_minima = min(len(lista_a), len(lista_b)).

Recorrer más de dos listas a la vez

Bueno, vamos a complicarlo un poco más, porque hay muchas veces en las que necesitaremos recorrer no dos, sino tres o más listas de manera simultánea.

La verdad es que se complica poco más porque todo lo que te he contado arriba es aplicable para múltiples listas.

La función zip puede recibir más de dos listas como parámetro y devolver una tupla con los valores de una posición de todas esas listas. Ejemplo:

lista_a = [1, 2, 3]
lista_b = [4, 5, 6]
lista_c = [7, 8, 9]
lista_d = [10, 11, 12]

for tupla in zip(lista_a, lista_b, lista_c, lista_d):
	print(tupla[0], tupla[1], tupla[2], tupla[3])

Si además del recorrido necesitamos los valores de los índices, porque queremos modificar algún valor de las listas, podemos hacer exactamente lo mismo que en el caso ya presentado de enumerate pero accediendo a múltiples listas dentro del cuerpo del bucle.

Otra posibilidad que puede tener utilidad es combinar el uso de enumerate y el de zip de la siguiente manera:

lista_a = [1, 2, 3]
lista_b = [4, 5, 6]
lista_c = [7, 8, 9]
lista_d = [10, 11, 12]

for indice, tupla in enumerate(zip(lista_a, lista_b, lista_c, lista_d)):
	print(tupla[0], tupla[1], tupla[2], tupla[3])
	#podemos acceder a través del índice también
	print(lista_a[indice], lista_b[indice], lista_c[indice], lista_d[indice])

Finalmente, podemos pensar en recorrer las listas utilizando directamente los índices:

lista_a = [1, 2, 3]
lista_b = [4, 5, 6]
lista_c = [7, 8, 9]

# recorrido con for
for indice in range(0, len(lista_a)):
	print(lista_a[indice], lista_b[indice], lista_c[indice])

#recorrido con while
indice = 0
while indice < len(lista_a): 
	print(lista_a[indice], lista_b[indice], lista_c[indice])
	indice += 1 

Agregar los datos

De todas maneras hay muchos casos en los que podemos evitar este tipo de recorridos múltiples, sobre todo cuando somos nosotros quienes diseñamos y programamos nuestro software o programa.

La idea es tener en cuenta en la fase de diseño que vamos a manejar datos que están relacionados entre sí y evitar utilizar una lista para cada dato.

Podemos utilizar una única lista y almacenar dentro una estructura que asocie los datos que están relacionados entre sí, como un objeto u otra lista.

Por ejemplo, y volviendo al ejemplo del comienzo de este artículo, si necesitas almacenar los nombres de los alumnos y sus calificaciones dentro de un curso, en lugar de utilizar una lista para los nombres y otra lista para las calificaciones (cosa que además complica el programa porque tenemos que garantizar que el orden de las listas sea coherente), puedes utilizar una única lista. Después, en cada posición guardas el nombre del alumno y su calificación. ¿Tiene sentido?

Lo vemos:

# opción "fea" que implica un recorrido múltiple y datos no asociados
alumnos = ['Juan', 'Pepe', 'Alfredo']
calificaciones = [5, 6.8, 4.5]

# opción "bonita" que permite un recorrido sencillo y datos asociados
alumnos = [['Juan', 5], ['Pepe', 6.8], ['Alfredo', 4.5]]

for alumno in alumnos:
	print(f'Nombre: {alumno[0]} - Calificación: {alumno[1]}')

Como ves, esta solución nos permite hacer un recorrido simple lo que nos permite no complicarnos. Además, nuestro diseño de datos está mejor realizado y es menos propenso a errores. Este código generará por pantalla la siguiente salida:

Nombre: Juan - Calificación: 5
Nombre: Pepe - Calificación: 6.8
Nombre: Alfredo - Calificación: 4.5

Ten en cuenta que esto podría mejorarse más utilizando orientación a objetos y clases en lugar de listas para guardar cada alumno, pero ese tema lo dejaremos para otro artículo.

Espero que esta entrada te haya servido de ayuda.

Nota final. Todos los ejemplos aquí propuestos también son válidos para tuplas, y en general para otros elementos iterables, y no únicamente para listas. Te hablo un poco más de listas, tuplas y diccionarios en este artículo sobre el uso de los paréntesis, los corchetes y las llaves en Python.

La Hoja de Referencia de Python – ¡Gratis!

La Hoja de Referencia de Python - Código Pitón
Consigue trucos, consejos y actualizaciones y, por supuesto, la Hoja de Referencia de Python gratis.



Antes de suscribirte consulta aquí la Información Básica sobre Protección de Datos. Responsable de los datos: Laura Otero Moreira. Finalidad de la recogida y tratamiento de los datos personales: enviarte boletín informativo de Python y comunicaciones comerciales. Legitimación: tu consentimiento. Destinatarios: no se ceden a terceros. Los datos se almacenan en los servidores de marketing (MailRelay). Derechos: podrás ejercer tus derechos de acceso, rectificación, limitación y supresión de datos en info @ codigopiton.com así como presentar una reclamación ante una autoridad de control. Más información en nuestra política de privacidad, encontrarás información adicional sobre la recopilación y el uso de tu información personal, incluida información sobre acceso, conservación, rectificación, eliminación, seguridad y otros temas.