Python - Previous and next values ​​inside a loop

How can i do such a thing in 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] } } 
+63
python loops
Jun 18 '09 at 10:23
source share
11 answers

That should do the trick.

 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] 

Here are the enumerate docs.

+76
Jun 18 '09 at 10:28
source share

Solutions so far only deal with lists, and most copies the list. In my experience, many times this was not possible.

Also, they are not related to the fact that you can have duplicate items in a list.

The title of your question reads “Previous and next values ​​inside the loop,” but if you follow most of the answers here inside the loop, you end up iterating the whole list again for each element to find it.

So I just created a function that. using the itertools module, itertools and splits the iterable and generates tuples with the previous and next elements together. Not exactly what your code does, but worth a look, because it can probably solve your problem.

 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) 

Then use it in a loop and you will have the previous and next elements:

 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 

Results, achievements:

 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 

It will work with any list of sizes (because it does not copy the list) and with any repeatable ones (files, sets, etc.). Thus, you can simply iterate over the sequence and have the previous and next elements in the loop. No need to search for an element in a sequence again

A brief explanation of the code:

  • tee used to efficiently create 3 independent iterators over the input sequence
  • chain connects two sequences into one; here it is used to add a singleton sequence [None] to prevs
  • islice used to create a sequence of all elements except the first, then chain used to add None to the end
  • Now there are 3 independent sequences based on some_iterable that look like this:
    • prevs : None, A, B, C, D, E
    • items : A, B, C, D, E
    • nexts : B, C, D, E, None
  • finally, izip used to change 3 sequences into one triplet sequence.

Please note that izip stops when any input sequence is exhausted, so the last prevs element will be ignored, and rightly so - there is no such element that its last element would be prev . We could try to remove the last elements from prevs but the izip behavior makes this redundant

Also note that tee , izip , islice and chain come from the itertools module; they work on their input sequences “on the fly” (lazily), which makes them efficient and does not create the need to have the entire sequence immediately in memory at any time.

In python 3 , an izip will appear when importing izip , you can use zip instead of izip . No need to import zip , this is predefined in python 3 - source code

+121
Jun 18 '09 at 11:12
source share

Using list comprehension, return 3 tuples with current, previous, and next elements:

 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)] 
+5
Sep 22 '15 at 10:40
source share

Here is the version using generators without boundary errors:

 def trios(input): input = iter(input) # make sure input is an iterator try: prev, current = input.next(), input.next() except StopIteration: return for next in input: 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) 

Note that the behavior of borders is that we never look for "foo" in the first or last element, unlike your code. Again, the semantics border is strange ... and hard to understand from your code :)

+2
Jun 18 '09 at 11:29
source share

using conditional expressions for brevity for 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) 
+1
Jun 18 '09 at 11:02
source share

For those who are looking for a solution to this problem with cyclic change of elements, the following may work:

 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) 
+1
May 08 '19 at
source share

You can simply use index in the list to find where somevalue , and then get the previous and next as needed:

 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'
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' 
0
Jun 18 '09 at 10:49
source share

AFAIK this should be pretty fast, but I have not tested it:

 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 

Usage example:

 >>> my_list = ['a', 'b', 'c'] >>> for prv, cur, nxt in iterate_prv_nxt(my_list): ... print prv, cur, nxt ... None ab abc bc None 
0
Mar 24 '14 at 11:40
source share

Using generators, this is pretty 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 ) 

Note that tests that include None and the same value as the signal value still work, because checking the signal value uses "is", and the signal is a value that Python does not intern. However, you can use any value of a singleton marker as a signal, which in some cases can simplify the user code.

0
Mar 05 '19 at 4:08
source share

I don’t know how this has not happened yet, since it uses only built-in functions and easily extends to other offsets:

 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) 
0
Jun 18 '19 at 17:19
source share

The pythonic and elegant way:

 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 
-one
Oct 09 '15 at 11:06
source share



All Articles