Como posso iterar em uma lista de objetos, acessando os itens anteriores, atuais e próximos? Gosta desse código C / C ++, em 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
ocorre exatamente uma vez na lista? Se ocorrer multiplicação, algumas abordagens aqui falharão ou apenas encontrarão a primeira. E se isso nunca ocorrer, outras abordagens falharão ou lançarão exceções como ValueError. Dar alguns casos de teste teria ajudado.Respostas:
Isso deve 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]
Aqui estão os documentos sobre a
enumerate
função.fonte
obj
enext_
será o mesmo objeto da última iteração, o que pode ter efeitos colaterais indesejados.index
deve ser executado1 ... (l-1)
, não0 ... l
como você fez aqui, e não há necessidade de cláusulas if com letras especiais. Btw, há um parâmetro,enumerate(..., start=1)
mas não paraend
. Portanto, não queremos realmente usarenumerate()
.As soluções até agora lidam apenas com listas, e a maioria está copiando a lista. Na minha experiência, muitas vezes isso não é possível.
Além disso, eles não lidam com o fato de que você pode ter elementos repetidos na lista.
O título da sua pergunta diz " Valores anteriores e próximos dentro de um loop ", mas se você executar a maioria das respostas aqui dentro de um loop, acabará iterando a lista inteira novamente em cada elemento para encontrá-lo.
Acabei de criar uma função que. usando o
itertools
módulo, divide e divide o iterável e gera tuplas com os elementos anteriores e seguintes juntos. Não é exatamente o que seu código faz, mas vale a pena dar uma olhada, porque provavelmente pode resolver seu 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)
Em seguida, use-o em um loop e você terá os itens anteriores e os próximos nele:
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
Os 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á com qualquer lista de tamanho (porque não copia a lista), e com qualquer iterável (arquivos, conjuntos, etc). Dessa forma, você pode apenas iterar sobre a sequência e ter os itens anteriores e posteriores disponíveis dentro do loop. Não há necessidade de pesquisar novamente o item na sequência.
Uma breve explicação do código:
tee
é usado para criar com eficiência 3 iteradores independentes sobre a sequência de entradachain
liga duas sequências em uma; é usado aqui para anexar uma sequência de elemento único[None]
aprevs
islice
é usado para fazer uma sequência de todos os elementos exceto o primeiro, entãochain
é usado para adicionar aNone
ao seu finalsome_iterable
que se parecem com:prevs
:None, A, B, C, D, E
items
:A, B, C, D, E
nexts
:B, C, D, E, None
izip
é usado para alterar 3 sequências em uma sequência de tripletos.Observe que
izip
para quando qualquer sequência de entrada se esgota, então o último elemento deprevs
será ignorado, o que é correto - não existe tal elemento que o último elemento seria seuprev
. Poderíamos tentar remover os últimos elementos,prevs
masizip
o comportamento torna isso redundanteObserve também que
tee
,izip
,islice
echain
vêm doitertools
módulo; eles operam em suas sequências de entrada em tempo real (preguiçosamente), o que os torna eficientes e não introduz a necessidade de ter toda a sequência na memória de uma vez a qualquer momento.Em
python 3
, ele mostrará um erro durante a importaçãoizip
, você pode usar emzip
vez deizip
. Não há necessidade de importaçãozip
, que é pré-definido napython 3
- fontefonte
izip
pode ser substituído pelazip
função embutida ;-)Usando uma compreensão de lista, retorne uma tupla de 3 com os elementos atual, anterior e seguinte:
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)]
fonte
Não sei como isso ainda não surgiu, pois usa apenas funções integradas e pode ser facilmente estendido para outros deslocamentos:
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)
fonte
Esta é uma versão que usa geradores sem erros de limite:
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))
Observe que o comportamento do limite é que nunca procuramos "foo" no primeiro ou no último elemento, ao contrário do seu código. Mais uma vez, a semântica de limite é estranha ... e difícil de entender em seu código :)
fonte
usando expressões condicionais para concisão 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)
fonte
Para quem procura uma solução para isso e também deseja fazer um ciclo entre os elementos, abaixo pode 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)
fonte
objects[i-1], objects[i], objects[i+1]
? ou um gerador? Parece totalmente obscurantista para mim. Também usa 3x a memória desnecessariamente, já que o PREV e o NEXT fazem cópias dos dados.i+1
abordagem funcionar para o último elemento da lista? O próximo elemento deve ser o primeiro. Eu fico fora dos limites.Usando geradores, é bastante simples:
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 )
Observe que os testes que incluem Nenhum e o mesmo valor do valor do sinal ainda funcionam, porque a verificação do valor do sinal usa "é" e o sinal é um valor que o Python não internaliza. Qualquer valor de marcador de singleton pode ser usado como um sinal, o que pode simplificar o código do usuário em algumas circunstâncias.
fonte
if iB is signal
para comparar objetos para igualdade, a menos que sinal = Nenhum, caso em que apenas escreva diretamenteNone
. Não useiter
como um nome de argumento, pois isso obscurece o embutidoiter()
. Idemnext
. De qualquer forma, a abordagem do gerador pode ser simplesmenteyield prev, curr, next_
is
vez de==
], é uma armadilha bem conhecida, aqui estão várias razões: você pode se safar com as strings, porque está contando com strings internas do cPython, mas mesmo assimv1 = 'monkey'; v2 = 'mon'; v3 = 'key
, entãov1 is (v2 + v3)
dáFalse
. E se seu código mudar para o uso de objetos em vez de ints / strings, o usois
será interrompido. Portanto, em geral, você deve usar==
para comparar igualdade.Duas soluções simples:
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
fonte
Você pode apenas usar
index
na lista para encontrar ondesomevalue
está e, em seguida, obter o anterior e o próximo conforme necessário: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'
fonte
AFAIK deve ser bem rápido, mas não testei:
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
Exemplo de uso:
>>> 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
fonte
Eu acho que isso funciona e não é 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]
fonte
Solução de estilo muito 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.")
fonte
Forma pitônica e 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
fonte
value
for no final. Além disso, retorna o último elemento comoprevious_value
sevalue
fosse o primeiro.previous_value
retornará o último elemento da lista enext_value
aumentaráIndexError
e isso é errovalue
pode ocorrer mais de uma vezobjects
, mas o uso.index()
só encontrará sua primeira ocorrência (ou ValueError se não ocorrer).