¿Cómo puedo iterar sobre una lista de objetos, accediendo a los elementos anterior, actual y siguiente? ¿Como este código C / C ++, en Python?
foo = somevalue;
previous = next = 0;
for (i=1; i<objects.length(); i++) {
if (objects[i]==foo) {
previous = objects[i-1];
next = objects[i+1];
}
}

fooocurra exactamente una vez en la lista? Si ocurre multiplicar, algunos enfoques aquí fallarán, o solo encontrarán el primero. Y si nunca ocurre, otros enfoques fallarán o lanzarán excepciones como ValueError. Dar algunos casos de prueba habría ayudado.Respuestas:
Esto debería funcionar.
foo = somevalue previous = next_ = None l = len(objects) for index, obj in enumerate(objects): if obj == foo: if index > 0: previous = objects[index - 1] if index < (l - 1): next_ = objects[index + 1]Aquí están los documentos sobre la
enumeratefunción.fuente
objynext_será el mismo objeto para la última iteración, lo que puede tener efectos secundarios no deseados.indexlo tanto, debería ejecutarse desde1 ... (l-1), no0 ... lcomo lo ha hecho aquí, y no hay necesidad de las cláusulas if en mayúsculas y minúsculas. Por cierto, hay un parámetroenumerate(..., start=1)pero no paraend. Entonces realmente no queremos usarenumerate().Las soluciones hasta ahora solo tratan con listas, y la mayoría está copiando la lista. En mi experiencia, muchas veces eso no es posible.
Además, no abordan el hecho de que puede tener elementos repetidos en la lista.
El título de su pregunta dice " Valores anteriores y siguientes dentro de un ciclo ", pero si ejecuta la mayoría de las respuestas aquí dentro de un ciclo, terminará iterando sobre la lista completa nuevamente en cada elemento para encontrarlo.
Así que acabo de crear una función que. usando el
itertoolsmódulo, divide y corta el iterable, y genera tuplas con los elementos anterior y siguiente juntos. No es exactamente lo que hace su código, pero vale la pena echarle un vistazo, porque probablemente pueda resolver su problema.from itertools import tee, islice, chain, izip def previous_and_next(some_iterable): prevs, items, nexts = tee(some_iterable, 3) prevs = chain([None], prevs) nexts = chain(islice(nexts, 1, None), [None]) return izip(prevs, items, nexts)Luego utilícelo en un bucle, y tendrá elementos anteriores y siguientes:
mylist = ['banana', 'orange', 'apple', 'kiwi', 'tomato'] for previous, item, nxt in previous_and_next(mylist): print "Item is now", item, "next is", nxt, "previous is", previousLos resultados:
Item is now banana next is orange previous is None Item is now orange next is apple previous is banana Item is now apple next is kiwi previous is orange Item is now kiwi next is tomato previous is apple Item is now tomato next is None previous is kiwiFuncionará con listas de cualquier tamaño (porque no copia la lista) y con cualquier iterable (archivos, conjuntos, etc.). De esta manera, puede iterar sobre la secuencia y tener los elementos anterior y siguiente disponibles dentro del ciclo. No es necesario volver a buscar el elemento en la secuencia.
Una breve explicación del código:
teese utiliza para crear de manera eficiente 3 iteradores independientes sobre la secuencia de entradachainenlaza dos secuencias en una; se usa aquí para agregar una secuencia de un solo elemento[None]aprevsislicese usa para hacer una secuencia de todos los elementos excepto el primero, luegochainse usa para agregar unNoneal finalsome_iterableese aspecto:prevs:None, A, B, C, D, Eitems:A, B, C, D, Enexts:B, C, D, E, Noneizipse usa para cambiar 3 secuencias en una secuencia de tripletes.Tenga en cuenta que se
izipdetiene cuando se agota cualquier secuencia de entrada, por lo que el último elemento deprevsse ignorará, lo cual es correcto: no existe tal elemento que el último elemento sería suyoprev. Podríamos intentar quitar los últimos elementos deprevsperoizipel comportamiento hace que eso sea redundanteTambién tenga en cuenta que
tee,izip,isliceychainvenir delitertoolsmódulo; operan en sus secuencias de entrada sobre la marcha (perezosamente), lo que las hace eficientes y no introduce la necesidad de tener la secuencia completa en la memoria de una vez en ningún momento.En
python 3, mostrará un error al importarizip, puede usar enziplugar deizip. No hay necesidad de importaciónzip, que está predefinido enpython 3- fuentefuente
izipse puede reemplazar con lazipfunción incorporada ;-)Usando una lista de comprensión, devuelve una tupla de 3 con los elementos actual, anterior y siguiente:
three_tuple = [(current, my_list[idx - 1] if idx >= 1 else None, my_list[idx + 1] if idx < len(my_list) - 1 else None) for idx, current in enumerate(my_list)]fuente
No sé cómo no ha surgido esto todavía, ya que solo usa funciones integradas y se puede extender fácilmente a otras compensaciones:
values = [1, 2, 3, 4] offsets = [None] + values[:-1], values, values[1:] + [None] for value in list(zip(*offsets)): print(value) # (previous, current, next) (None, 1, 2) (1, 2, 3) (2, 3, 4) (3, 4, None)fuente
Aquí hay una versión que usa generadores sin errores de límites:
def trios(iterable): it = iter(iterable) try: prev, current = next(it), next(it) except StopIteration: return for next in it: yield prev, current, next prev, current = current, next def find_prev_next(objects, foo): prev, next = 0, 0 for temp_prev, current, temp_next in trios(objects): if current == foo: prev, next = temp_prev, temp_next return prev, next print(find_prev_next(range(10), 1)) print(find_prev_next(range(10), 0)) print(find_prev_next(range(10), 10)) print(find_prev_next(range(0), 10)) print(find_prev_next(range(1), 10)) print(find_prev_next(range(2), 10))Tenga en cuenta que el comportamiento de los límites es que nunca buscamos "foo" en el primer o último elemento, a diferencia de su código. Nuevamente, la semántica de los límites es extraña ... y es difícil de comprender a partir de su código :)
fuente
usando expresiones condicionales para concisión para python> = 2.5
def prenext(l,v) : i=l.index(v) return l[i-1] if i>0 else None,l[i+1] if i<len(l)-1 else None # example x=range(10) prenext(x,3) >>> (2,4) prenext(x,0) >>> (None,2) prenext(x,9) >>> (8,None)fuente
Para cualquiera que busque una solución a esto y también quiera alternar los elementos, a continuación podría funcionar:
from collections import deque foo = ['A', 'B', 'C', 'D'] def prev_and_next(input_list): CURRENT = input_list PREV = deque(input_list) PREV.rotate(-1) PREV = list(PREV) NEXT = deque(input_list) NEXT.rotate(1) NEXT = list(NEXT) return zip(PREV, CURRENT, NEXT) for previous_, current_, next_ in prev_and_next(foo): print(previous_, current_, next)fuente
objects[i-1], objects[i], objects[i+1]? o un generador? Simplemente me parece totalmente oscurantista. También utiliza innecesariamente la memoria 3x ya que PREV y NEXT hacen copias de los datos.i+1enfoque funcione para el último elemento de la lista? El siguiente elemento debería ser el primero. Me salgo de los límites.Usando generadores, es bastante simple:
signal = ['→Signal value←'] def pniter( iter, signal=signal ): iA = iB = signal for iC in iter: if iB is signal: iB = iC continue else: yield iA, iB, iC iA = iB iB = iC iC = signal yield iA, iB, iC if __name__ == '__main__': print('test 1:') for a, b, c in pniter( range( 10 )): print( a, b, c ) print('\ntest 2:') for a, b, c in pniter([ 20, 30, 40, 50, 60, 70, 80 ]): print( a, b, c ) print('\ntest 3:') cam = { 1: 30, 2: 40, 10: 9, -5: 36 } for a, b, c in pniter( cam ): print( a, b, c ) for a, b, c in pniter( cam ): print( a, a if a is signal else cam[ a ], b, b if b is signal else cam[ b ], c, c if c is signal else cam[ c ]) print('\ntest 4:') for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ]): print( a, b, c ) print('\ntest 5:') for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ], ['sig']): print( a, b, c ) print('\ntest 6:') for a, b, c in pniter([ 20, ['→Signal value←'], None, '→Signal value←', 60, 70, 80 ], signal ): print( a, b, c )Tenga en cuenta que las pruebas que incluyen None y el mismo valor que el valor de la señal aún funcionan, porque la verificación del valor de la señal usa "es" y la señal es un valor que Python no realiza. Sin embargo, cualquier valor de marcador singleton puede usarse como señal, lo que podría simplificar el código de usuario en algunas circunstancias.
fuente
if iB is signalpara comparar objetos por igualdad, a menos que signal = None, en cuyo caso simplemente escriba directamenteNoneya. No lo useitercomo nombre de argumento, ya que ensombrece el incorporadoiter(). Ídemnext. De todos modos, el enfoque del generador puede ser simplementeyield prev, curr, next_islugar de==], es una trampa bien conocida, aquí hay varias razones por las que: puede salirse con la suya para las cadenas, porque está confiando en las cadenas de internación de cPython, pero incluso entoncesv1 = 'monkey'; v2 = 'mon'; v3 = 'key, entoncesv1 is (v2 + v3)daFalse. Y si su código alguna vez cambia a usar objetos en lugar de ints / strings, el usoisse romperá. Entonces, en general, debe usar==para comparar igualdad.Dos sencillas soluciones:
alist = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five'] prev = alist[0] curr = alist[1] for nxt in alist[2:]: print(f'prev: {prev}, curr: {curr}, next: {nxt}') prev = curr curr = nxt Output[1]: prev: Zero, curr: One, next: Two prev: One, curr: Two, next: Three prev: Two, curr: Three, next: Four prev: Three, curr: Four, next: Fivealist = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five'] prev = None curr = alist[0] for nxt in alist[1:] + [None]: print(f'prev: {prev}, curr: {curr}, next: {nxt}') prev = curr curr = nxt Output[2]: prev: None, curr: Zero, next: One prev: Zero, curr: One, next: Two prev: One, curr: Two, next: Three prev: Two, curr: Three, next: Four prev: Three, curr: Four, next: Five prev: Four, curr: Five, next: Nonefuente
Puede usar
indexen la lista para encontrar dóndesomevalueestá y luego obtener el anterior y el siguiente según sea necesario:def find_prev_next(elem, elements): previous, next = None, None index = elements.index(elem) if index > 0: previous = elements[index -1] if index < (len(elements)-1): next = elements[index +1] return previous, next foo = 'three' list = ['one','two','three', 'four', 'five'] previous, next = find_prev_next(foo, list) print previous # should print 'two' print next # should print 'four'fuente
AFAIK, esto debería ser bastante rápido, pero no lo probé:
def iterate_prv_nxt(my_list): prv, cur, nxt = None, iter(my_list), iter(my_list) next(nxt, None) while True: try: if prv: yield next(prv), next(cur), next(nxt, None) else: yield None, next(cur), next(nxt, None) prv = iter(my_list) except StopIteration: breakUso de ejemplo:
>>> my_list = ['a', 'b', 'c'] >>> for prv, cur, nxt in iterate_prv_nxt(my_list): ... print prv, cur, nxt ... None a b a b c b c Nonefuente
Creo que esto funciona y no es complicado.
array= [1,5,6,6,3,2] for i in range(0,len(array)): Current = array[i] Next = array[i+1] Prev = array[i-1]fuente
Solución de estilo muy C / C ++:
foo = 5 objectsList = [3, 6, 5, 9, 10] prev = nex = 0 currentIndex = 0 indexHigher = len(objectsList)-1 #control the higher limit of list found = False prevFound = False nexFound = False #main logic: for currentValue in objectsList: #getting each value of list if currentValue == foo: found = True if currentIndex > 0: #check if target value is in the first position prevFound = True prev = objectsList[currentIndex-1] if currentIndex < indexHigher: #check if target value is in the last position nexFound = True nex = objectsList[currentIndex+1] break #I am considering that target value only exist 1 time in the list currentIndex+=1 if found: print("Value %s found" % foo) if prevFound: print("Previous Value: ", prev) else: print("Previous Value: Target value is in the first position of list.") if nexFound: print("Next Value: ", nex) else: print("Next Value: Target value is in the last position of list.") else: print("Target value does not exist in the list.")fuente
Manera pitónica y elegante:
objects = [1, 2, 3, 4, 5] value = 3 if value in objects: index = objects.index(value) previous_value = objects[index-1] next_value = objects[index+1] if index + 1 < len(objects) else Nonefuente
valueestá al final. Además, devuelve el último elemento comoprevious_valuesi fueravalueel primero.previous_valuedevolverá el último elemento de la lista ynext_valueaumentaráIndexErrory eso es un errorvaluepodría ocurrir más de una vezobjects, pero el uso.index()solo encontrará su primera ocurrencia (o ValueError si no ocurre).