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?

Asked By: velut luna

||

Answers:

  1. Use a dictionary:

    d = {}
    for i in range(5, 10):
        d[i] = f"something{i}"
    
  2. Or a dictionary comprehension:

    d = {i:f"something{i}" for i in range(5, 10)}
    
  3. 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
Answered By: bcorso

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
Answered By: Martin Konecny

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]
Answered By: Óscar López

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.

Answered By: shx2
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.