How can I iterate over a list of objects, accessing the previous, current, and next items? Like this C/C++ code, 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];
}
}
Using generators, it is quite 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 the check for the signal value uses "is" and the signal is a value that Python doesn't intern. Any singleton marker value can be used as a signal, though, which might simplify user code in some circumstances.
Here's a version using generators with no boundary errors:
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))
Please notice that the boundary behavior is that we never look for "foo" in the first or last element, unlike your code. Again, the boundary semantics are strange...and are hard to fathom from your code :)
For anyone looking for a solution to this with also wanting to cycle the elements, below might 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)
using conditional expressions for conciseness 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)
Using a list comprehension, return a 3-tuple 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)]
AFAIK this should be pretty fast, but I didn't test 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
Example usage:
>>> 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
I think this works and not complicated
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]
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
You could just use index
on the list to find where somevalue
is 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'
I don't know how this hasn't come up yet since it uses only built-in functions and is easily extendable 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)
Two simple solutions:
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
Solutions until now only deal with lists, and most are copying the list. In my experience a lot of times that isn't possible.
Also, they don't deal with the fact that you can have repeated elements in the list.
The title of your question says "Previous and next values inside a loop", but if you run most answers here inside a loop, you'll end up iterating over the entire list again on each element to find it.
So I've just created a function that. using the itertools
module, splits and slices the iterable, and generates tuples with the previous and next elements together. Not exactly what your code does, but it is worth taking 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'll have previous and next items in it:
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
The results:
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'll work with any size list (because it doesn't copy the list), and with any iterable (files, sets, etc). This way you can just iterate over the sequence, and have the previous and next items available inside the loop. No need to search again for the item in the sequence.
A short explanation of the code:
tee
is used to efficiently create 3 independent iterators over the input sequencechain
links two sequences into one; it's used here to append a single-element sequence [None]
to prevs
islice
is used to make a sequence of all elements except the first, then chain
is used to append a None
to its endsome_iterable
that look like:
prevs
: None, A, B, C, D, E
items
: A, B, C, D, E
nexts
: B, C, D, E, None
izip
is used to change 3 sequences into one sequence of triplets.Note that izip
stops when any input sequence gets exhausted, so the last element of prevs
will be ignored, which is correct - there's no such element that the last element would be its prev
. We could try to strip off the last elements from prevs
but izip
's behaviour makes that redundant
Also note that tee
, izip
, islice
and chain
come from the itertools
module; they operate on their input sequences on-the-fly (lazily), which makes them efficient and doesn't introduce the need of having the whole sequence in memory at once at any time.
In python 3
, it will show an error while importing izip
,you can use zip
instead of izip
. No need to import zip
, it is predefined in python 3
- source
Very C/C++ style solution:
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.")
Source: Stackoverflow.com