Array in python with arbitrary index
Question:
I am a beginner of python.
I want to have arrays with arbitrary index running from p
to q
, instead of from 0.
How to create an array with q-p+1
elements with index from p
to q
?
Answers:
-
Use a dictionary:
d = {}
for i in range(5, 10):
d[i] = f"something{i}"
-
Or a dictionary comprehension:
d = {i:f"something{i}" for i in range(5, 10)}
-
Or an OrderedDict if insertion order matters and you’re using Python 3.6 or lower:
from collections import OrderedDict
d = OrderedDict()
for i in range(5, 10):
d[i] = f"something{i}"
Usage
Elements can be retrieved with the same syntax as a list (Ideone Example):
print(d[7])
Output: something7
If you want to iterate the values of an OrderedDict use (Ideone Example):
for x in d.values():
print(x)
Output:
something5
something6
something7
something8
something9
You can’t do this with a list (and assuming you don’t want to use a dictionary because you want the elements ordered) – however you can simulate it.
p = 10
q = 20
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#takes in index from 10 to 19 - throws exception otherwise
def get_elem(index):
index = index-p
if index > 0 and index < len(l):
return l[index-p]
else:
raise IndexError("list index out of range")
print get_elem(13) # prints 4
There’s no built-in syntax for what you’re asking. It’ll be easier if you just transform the desired range of indexes into the indexes of a standard list. For instance:
p = 10
q = 20
lst = [None for _ in range(q-p+1)] # initialize a list for the desired range
Now, to access any idx
position in the range do this:
lst[idx-p]
For example:
lst[10-p] = 100
lst[10-p]
=> 100
lst[20-p] = 200
lst[20-p]
=> 200
Under the hood the list will look like this after the two assignments:
[100, None, None, None, None, None, None, None, None, None, 200]
You can create a subclass of list
which adjusts the index on item-access:
class ListWithOffset(list):
def __init__(self, offset, *a, **kw):
self.offset = offset
super().__init__(*a, **kw)
def __getitem__(self, i):
return super().__getitem__(self, self._adjust_idx(i))
def __setitem__(self, i, value):
return super().__setitem__(self, self._adjust_idx(i), value)
def __delitem__(self, i):
return super().__delitem__(self, self._adjust_idx(i))
def _adjust_idx(self, i):
if isinstance(i, slice):
return slice(i.start - self.offset if i.start is not None else None,
i.stop - self.offset if i.stop is not None else None,
i.step)
else:
return i - self.offset
(edit: forgot to handle slicing)
Note it is not necessary to specify the end-index explicitly. It can change as the size of your list changes, and can be defined as mylist.offset + len(mylist)
at any point in time.
Also note I kept the code snippet here in its simplest form, but for this to be useful you’d also need to handle the cases where the index passed is smaller than the offset. This implementation will likely return unexpected results (corresponding to list
behavior when accessing negative indices), which is probably not ideal.
I am a beginner of python.
I want to have arrays with arbitrary index running from p
to q
, instead of from 0.
How to create an array with q-p+1
elements with index from p
to q
?
-
Use a dictionary:
d = {} for i in range(5, 10): d[i] = f"something{i}"
-
Or a dictionary comprehension:
d = {i:f"something{i}" for i in range(5, 10)}
-
Or an OrderedDict if insertion order matters and you’re using Python 3.6 or lower:
from collections import OrderedDict d = OrderedDict() for i in range(5, 10): d[i] = f"something{i}"
Usage
Elements can be retrieved with the same syntax as a list (Ideone Example):
print(d[7])
Output: something7
If you want to iterate the values of an OrderedDict use (Ideone Example):
for x in d.values():
print(x)
Output:
something5
something6
something7
something8
something9
You can’t do this with a list (and assuming you don’t want to use a dictionary because you want the elements ordered) – however you can simulate it.
p = 10
q = 20
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#takes in index from 10 to 19 - throws exception otherwise
def get_elem(index):
index = index-p
if index > 0 and index < len(l):
return l[index-p]
else:
raise IndexError("list index out of range")
print get_elem(13) # prints 4
There’s no built-in syntax for what you’re asking. It’ll be easier if you just transform the desired range of indexes into the indexes of a standard list. For instance:
p = 10
q = 20
lst = [None for _ in range(q-p+1)] # initialize a list for the desired range
Now, to access any idx
position in the range do this:
lst[idx-p]
For example:
lst[10-p] = 100
lst[10-p]
=> 100
lst[20-p] = 200
lst[20-p]
=> 200
Under the hood the list will look like this after the two assignments:
[100, None, None, None, None, None, None, None, None, None, 200]
You can create a subclass of list
which adjusts the index on item-access:
class ListWithOffset(list):
def __init__(self, offset, *a, **kw):
self.offset = offset
super().__init__(*a, **kw)
def __getitem__(self, i):
return super().__getitem__(self, self._adjust_idx(i))
def __setitem__(self, i, value):
return super().__setitem__(self, self._adjust_idx(i), value)
def __delitem__(self, i):
return super().__delitem__(self, self._adjust_idx(i))
def _adjust_idx(self, i):
if isinstance(i, slice):
return slice(i.start - self.offset if i.start is not None else None,
i.stop - self.offset if i.stop is not None else None,
i.step)
else:
return i - self.offset
(edit: forgot to handle slicing)
Note it is not necessary to specify the end-index explicitly. It can change as the size of your list changes, and can be defined as mylist.offset + len(mylist)
at any point in time.
Also note I kept the code snippet here in its simplest form, but for this to be useful you’d also need to handle the cases where the index passed is smaller than the offset. This implementation will likely return unexpected results (corresponding to list
behavior when accessing negative indices), which is probably not ideal.