¿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];
}
}
foo
ocurra 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
enumerate
función.fuente
obj
ynext_
será el mismo objeto para la última iteración, lo que puede tener efectos secundarios no deseados.index
lo tanto, debería ejecutarse desde1 ... (l-1)
, no0 ... l
como 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
itertools
mó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", previous
Los 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 kiwi
Funcionará 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:
tee
se utiliza para crear de manera eficiente 3 iteradores independientes sobre la secuencia de entradachain
enlaza dos secuencias en una; se usa aquí para agregar una secuencia de un solo elemento[None]
aprevs
islice
se usa para hacer una secuencia de todos los elementos excepto el primero, luegochain
se usa para agregar unNone
al finalsome_iterable
ese aspecto:prevs
:None, A, B, C, D, E
items
:A, B, C, D, E
nexts
:B, C, D, E, None
izip
se usa para cambiar 3 secuencias en una secuencia de tripletes.Tenga en cuenta que se
izip
detiene cuando se agota cualquier secuencia de entrada, por lo que el último elemento deprevs
se ignorará, lo cual es correcto: no existe tal elemento que el último elemento sería suyoprev
. Podríamos intentar quitar los últimos elementos deprevs
peroizip
el comportamiento hace que eso sea redundanteTambién tenga en cuenta que
tee
,izip
,islice
ychain
venir delitertools
mó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 enzip
lugar deizip
. No hay necesidad de importaciónzip
, que está predefinido enpython 3
- fuentefuente
izip
se puede reemplazar con lazip
funció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+1
enfoque 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 signal
para comparar objetos por igualdad, a menos que signal = None, en cuyo caso simplemente escriba directamenteNone
ya. No lo useiter
como nombre de argumento, ya que ensombrece el incorporadoiter()
. Ídemnext
. De todos modos, el enfoque del generador puede ser simplementeyield prev, curr, next_
is
lugar 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 usois
se 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: Five
alist = ['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: None
fuente
Puede usar
index
en la lista para encontrar dóndesomevalue
está 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: break
Uso 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 None
fuente
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 None
fuente
value
está al final. Además, devuelve el último elemento comoprevious_value
si fueravalue
el primero.previous_value
devolverá el último elemento de la lista ynext_value
aumentaráIndexError
y eso es un errorvalue
podría ocurrir más de una vezobjects
, pero el uso.index()
solo encontrará su primera ocurrencia (o ValueError si no ocurre).