Obtener el siguiente elemento mientras recorre una lista

5 minutos de lectura
li = [0, 1, 2, 3]

running = True
while running:
    for elem in li:
        thiselem = elem
        nextelem = li[li.index(elem)+1]

Cuando esto llega al último elemento, un IndexError se genera (como es el caso de cualquier lista, tupla, diccionario o cadena que se itera). En realidad quiero en ese punto para nextelem A igual li[0]. Mi solución bastante engorrosa para esto fue

while running:
    for elem in li:
        thiselem = elem
        nextelem = li[li.index(elem)-len(li)+1]   # negative index

¿Hay una mejor manera de hacer esto?

  • Considere dejar de lado el ciclo while. Parece irrelevante para la pregunta. Si es relevante, considere explicar por qué.

    – Jason R. Coombs

    30 de enero de 2010 a las 13:36

  • Quiero recorrer una lista indefinidamente, de ahí la combinación de bucle while/for. Lo siento, no expliqué eso.

    ignorante

    30 de enero de 2010 a las 13:50


  • Supongo que también, idealmente, le gustaría poder detenerse en la mitad del ciclo en lugar de solo al final.

    – Omnifaro

    30 de enero de 2010 a las 13:55

  • Sí. Estoy seguro de que podría usar break para eso.

    ignorante

    30 de enero de 2010 a las 14:45

Después de pensarlo detenidamente, creo que esta es la mejor manera. Te permite bajarte en el medio fácilmente sin usar break, que creo que es importante y requiere un cálculo mínimo, por lo que creo que es el más rápido. Tampoco requiere eso li ser una lista o tupla. Podría ser cualquier iterador.

from itertools import cycle

li = [0, 1, 2, 3]

running = True
licycle = cycle(li)
# Prime the pump
nextelem = next(licycle)
while running:
    thiselem, nextelem = nextelem, next(licycle)

Dejo las otras soluciones aquí para la posteridad.

Todo ese sofisticado iterador tiene su lugar, pero no aquí. Utilice el operador %.

li = [0, 1, 2, 3]

running = True
while running:
    for idx, elem in enumerate(li):
        thiselem = elem
        nextelem = li[(idx + 1) % len(li)]

Ahora, si tiene la intención de recorrer infinitamente una lista, simplemente haga esto:

li = [0, 1, 2, 3]

running = True
idx = 0
while running:
    thiselem = li[idx]
    idx = (idx + 1) % len(li)
    nextelem = li[idx]

Creo que es más fácil de entender que la otra solución que involucra tee, y probablemente más rápido también. Si está seguro de que la lista no cambiará de tamaño, puede guardar una copia de len(li) y usa eso.

Esto también le permite bajarse fácilmente de la rueda de la fortuna en el medio en lugar de tener que esperar a que el balde vuelva al fondo. Las otras soluciones (incluida la suya) requieren que compruebe running en medio de la for bucle y luego break.

  • +1 Sí, esto también es simple y efectivo. No hay necesidad de estas soluciones funcionales innecesariamente complejas para problemas que tienen soluciones de procedimiento extremadamente simples. ¡Python no es un lenguaje funcional!

    –Mark Byers

    30 de enero de 2010 a las 13:37

  • @Mark Byers, ¡gracias! Creo que hay espacio para una solución bastante funcional si la lista es bastante pequeña, y la agregué. todo el rato con running no es funcional, y tratar de calzarlo en una solución funcional es feo.

    – Omnifaro

    30 de enero de 2010 a las 13:53

  • @Mark Byers: Allí, la respuesta que creo que es mejor usa itertools, pero de una manera muy minimalista. Creo que es muy rápido y fácil de entender.

    – Omnifaro

    30 de enero de 2010 a las 14:13

  • Soy tan perfeccionista. Es irritante que después de 8 ediciones se convierta en una respuesta wiki de la comunidad. suspiro

    – Omnifaro

    30 de enero de 2010 a las 14:25

  • ¡me encanta esto! (idx + 1) % len(li)

    – Jonathan

    4 de febrero de 2018 a las 16:02

while running:
    for elem,next_elem in zip(li, li[1:]+[li[0]]):
        ...

  • Este es mi segundo favorito, después del mío. 🙂 Simple es lo mejor, pero si la lista es bastante grande, esto crea dos copias de ella.

    – Omnifaro

    30 de enero de 2010 a las 13:34

  • Una de las cosas más pitónicas que he visto.

    – Vicente Alex

    18 mayo 2022 a las 21:22

Puede usar un iterador cíclico por pares:

from itertools import izip, cycle, tee

def pairwise(seq):
    a, b = tee(seq)
    next(b)
    return izip(a, b)

for elem, next_elem in pairwise(cycle(li)):
    ...

  • Esto es bueno. Incorpora el ciclo while y el ciclo for en un ciclo conciso, que itera continuamente sobre los pares adyacentes en la lista y termina al final.

    – Jason R. Coombs

    30 de enero de 2010 a las 13:37

  • +1 por usar pairwise: una función de biblioteca no oficial que existe solo en la documentación de itertools. Entonces, aunque no puede importarlo, se sabe que funciona.

    – Jason R. Coombs

    30 de enero de 2010 a las 13:39

  • Creo que oscurece la prueba para ejecutarse dentro del ciclo for y requiere el uso de break no es tan buena idea.

    – Omnifaro

    30 de enero de 2010 a las 13:45

while running:
    lenli = len(li)
    for i, elem in enumerate(li):
        thiselem = elem
        nextelem = li[(i+1)%lenli]

Una forma bastante diferente de resolver esto:

   li = [0,1,2,3]

   for i in range(len(li)):

       if i < len(li)-1:

           # until end is reached
           print 'this', li[i]
           print 'next', li[i+1]

       else:

           # end
           print 'this', li[i]

Avatar de usuario de Nirmal
Nirmal

Usa el método zip en Python. Esta función devuelve una lista de tuplas, donde la i-ésima tupla contiene el i-ésimo elemento de cada una de las secuencias de argumentos o iterables

    while running:
        for thiselem,nextelem in zip(li, li[1 : ] + li[ : 1]):
            #Do whatever you want with thiselem and nextelem         

Avatar de usuario de Mehdi Khlifi
Mehdi Jlifi

Tan simple como esto:

li = [0, 1, 2, 3]
while 1:
   for i, item in enumerate(x):
      k = i + 1 if i != len(x) - 1 else 0
      print('Current index {} : {}'.format(i,li[k]))

¿Ha sido útil esta solución?