0

Suppose I have a nested list (ndlist) similar to an N-D array of arbitrary dimensions (let ndim be the number of dimensions) and a tuple indexes with len(indexes) == ndim. If ndlist was an N-D array I could do the following:

ndlist[indexes] = (some object)

What is the equivalent for a list? Note that ndim is arbitrary so I can't hardcode it like this:

ndlist[indexes[0]][indexes[1]]... = (some object)

Here's an example for ndim == 3:

ndlist = [[[10, 10], [10, 10]],[[10, 10], [10, 10]]] % 3-D (2x2x2) list with all elements equal to 10

When I know ndim beforehand I can edit the (0,0,0) element of ndlist like this:

ndlist[0][0][0] = 11 %changing from 10 to 11 requires 3 [0]s in sequence

Now suppose ndims == 4 (4-dimensional list). Editing the the (0,0,0,0) element of ndlist would require something like this:

ndlist[0][0][0][0] = 11 %change to 11 requires 4 [0]s in sequence

And for arbitrary ndims:

ndlist[0][0][0]...[0] = 11 %change to 11 requires ndim [0]s in sequence

As you see, I can't index the list that way for the general case where ndim is not known in advance, as it requires to type as many [0] as ndim.

If instead of the list I had an array like this:

ndarray = np.array(ndlist)

Accessing the (0, 0, 0, ... ,0) would not be an issue since I can using a tuple to index all dimensions simultaneously like that:

% 3d case

indexes = (0,0,0)

ndarray[indexes]

% 4d case

indexes = (0,0,0,0)

ndarray[indexes]

% n-d case

indexes = (0, 0, 0, ... ,0) % I can generate this with code

ndarray[indexes] = 11

Is there a way to index a list with a single tuple like that? Even better, can the tuple hold slices instead of indexes? For instance arrays also allow this:

ndarray[0:2, 0, 0] = np.array([0, 0])


The only solution have found to my problem is to use recursion to index one dimension at a time. Is there a better solution? Thanks!

Phil
  • 3
  • 2
  • I cannot understand the question. Are you looking for a way to get shape of the nested list regardless of the number of dimensions? Similar to `shape` of a `numpy.ndarray`? – MSH Nov 04 '21 at 06:20
  • Can you please rewrite your question with an example? It's difficult to understand the question. What I understood is that you want to access an element within an n-dimensional list (python list) using indices which are a type tuple, Am I right? If so, then kindly define your indices, list and the desired output. Thank you ! – Nouman Ahsan Nov 04 '21 at 07:15
  • @NoumanAhsan you are right. This is exactly what I am trying to do. Ideally, I dont want to just index but also slice. I have added an example to previous post. – Phil Nov 04 '21 at 17:02
  • @MSH I am sorry my question was not clear enough. I have added an example and should be ok now. Thanks. – Phil Nov 04 '21 at 17:06

1 Answers1

0

Now I understood the problem. If you are willing to have a function donig that for you it will be easy. Otherwise you'll need to create your own list type.

Function

You will need a function which takes a list and n (unknown) number of elements. The n means you will need *argv

The function will get the list and get the ith element in n (argv).

def get_element(the_list, *argv):
    sub_list = the_list.copy()
    for i in argv:
        sub_list = sub_list[i]

    return sub_list

Note: The function copies the original list to make sure it's not changed.

Note: You will need to handle the index out of range error too. Right now it will raise an error of TypeError: 'blabla' object is not subscriptable.

Your own list type

Here you will create a class with any name (let's sat TypedList) which inherits from list and override the __getitem__ method.

class TypedList(list):
    def __getitem__(self, keys):
        sub_list = self.copy()
        for i in keys:
            sub_list = sub_list[i]

        return sub_list


if __name__ == '__main__':
    ndlist = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
    lst = TypedList(ndlist)
    print(lst[1, 1, 0])

Here you will need handle the index out of range error too.

Edit after the comment.

You are very right that I actually did not answer the question. And Since you already accepted my answer I am going to find a way to do it.

Some edits

My implementation has a small (Actually huge) problem in it. If you use just an integer as the index it will fail. So the code must change as:

class TypedList(list):

    def __getitem__(self, keys):

        sub_list = self.copy()

        if isinstance(keys, int):
            return sub_list[keys]
        for i in keys:
            sub_list = sub_list[i]

        return sub_list

Now. I must clarify that

Python doesn't copy objects you pass during a function call ever.

What does that mean? IF you modify the passed object to a function, the object itself will change. Notice how I copied lists first then worked on them. See: https://stackoverflow.com/a/575337/2681662

We are going to get advantage of this with writing a function that actually modifies the passed object:

def set_value(the_list, keys, value):
    for i in range(len(keys) - 1):
        the_list = the_list[keys[i]]
    the_list[keys[-1]] = value

Now the only thing to do is, to somehow manage to make this function a method of our class and use it from __setitem__

class TypedList(list):
    def __set(self, the_list, keys, value):
        for i in range(len(keys) - 1):
            the_list = the_list[keys[i]]
        the_list[keys[-1]] = value

    def __setitem__(self, keys, value):
        if isinstance(keys, int):
            super().__setitem__(keys, value) # <- This has to be done. Otherwise maximum recursion depth would occurre...
        else:
            self.__set(self, keys, value)

    def __getitem__(self, keys):

        sub_list = self.copy()

        if isinstance(keys, int):
            return sub_list[keys]
        for i in keys:
            sub_list = sub_list[i]

        return sub_list


if __name__ == '__main__':
    ndlist = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
    lst = TypedList(ndlist)
    lst[1, 1, 0] = 22
    print(lst)

Here we have a private method called __set and will be called by __setitem__ to modify self which is a type of list.

MSH
  • 1,743
  • 2
  • 14
  • 22
  • @ MSH thanks a lot! I can definitely see how the TypedList class will return the ndlist value at the specified index but at a first glance I don't think I can use it to modify the value at the specified index. I would really appreciate it if you could recommend some reading to understand what python is doing under the hood when indexing/slicing a list (you can assume I have good understanding of low level implementation of data structures) – Phil Nov 04 '21 at 18:25
  • I updated the answer. Believe me I am not that good at `low level implementation of data structure`. :) – MSH Nov 04 '21 at 21:04
  • 1
    Wow! Thanks so much for your time! I really appreciate it. For what it's worth, I promise to come back and upvote your answer when I get to rep 5. – Phil Nov 05 '21 at 16:24