Say I have a list,
l = [1, 2, 3, 4, 5, 6, 7, 8]
I want to grab the index of an arbitrary element and the values of its neighbors. For examp
a = [2,3,5,7,11,13]
def env (l, n, count):
from itertools import cycle, islice
index = l.index(n) + len(l)
aux = islice (cycle (l), index - count, index + count + 1)
return list(aux)
Behaves as follows
>>> env (a, 2,1)
[13, 2, 3]
>>> env (a,13,2)
[7, 11, 13, 2, 3]
>>> env (a,7,0)
[7]
In case you do not want to wrap around, the most Pythonic answer would be to use slices. Missing neighbor substituted with None. E.g.:
def nbrs(l, e):
i = l.index(e)
return (l[i-1:i] + [None])[0], (l[i+1:i+2] + [None])[0]
This is how the function can work:
>>> nbrs([2,3,4,1], 1)
(4, None)
>>> nbrs([1,2,3], 1)
(None, 2)
>>> nbrs([2,3,4,1,5,6], 1)
(4, 5)
>>> nbrs([], 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in nbrs
ValueError: 1 is not in list
The easiest way to wrap around a fixed length list is with the % (modulo) operator
list_element = my_list[idx % len(my_list)]
but anyway look at http://docs.python.org/library/itertools.html
from itertools import cycle
for p in cycle([1,2,3]):
print "endless cycle:", p
Using the modulo method that others have mentioned I have created a class with a property that implements a circular list.
class Circle:
"""Creates a circular array of numbers
>>> c = Circle(30)
>>> c.position
-1
>>> c.position = 10
>>> c.position
10
>>> c.position = 20
>>> c.position
20
>>> c.position = 30
>>> c.position
0
>>> c.position = -5
>>> c.position
25
>>>
"""
def __init__(self, size):
if not isinstance(size, int): # validating length
raise TypeError("Only integers are allowed")
self.size = size
@property
def position(self):
try:
return self._position
except AttributeError:
return -1
@position.setter
def position(self, value):
positions = [x for x in range(0, self.size)]
i = len(positions) - 1
k = positions[(i + value + 1) % len(positions)]
self._position = k
The typical way to fit values to a certain range is to use the %
operator:
k = l[(i + 1) % len(l)]
If you want it as a class, I whipped up this quick CircularList:
import operator
class CircularList(list):
def __getitem__(self, x):
if isinstance(x, slice):
return [self[x] for x in self._rangeify(x)]
index = operator.index(x)
try:
return super().__getitem__(index % len(self))
except ZeroDivisionError:
raise IndexError('list index out of range')
def _rangeify(self, slice):
start, stop, step = slice.start, slice.stop, slice.step
if start is None:
start = 0
if stop is None:
stop = len(self)
if step is None:
step = 1
return range(start, stop, step)
It supports slicing, so
CircularList(range(5))[1:10] == [1, 2, 3, 4, 0, 1, 2, 3, 4]