6

I just started with python and very soon wondered if indexing a nested list with a tuple was possible. Something like: elements[(1,1)]

One example where I wanted to do that was something similar to the code below in which I save some positions of the matrix that I will later need to access in a tuple called index.

index = ( (0,0), (0,2), (2,0), (2,2) )

elements = [ [ 'a', 'b', 'c'],
             [ 'c', 'd', 'e'],
             [ 'f', 'g', 'h'] ]

for i in index:
    print (elements [ i[0] ] [ i[1] ])

    # I would like to do this:
    # print(elements[i])

It seems like a useful feature. Is there any way of doing it? Or perhaps a simple alternative?

3
  • 1
    What exactly is your question? Your example works as advertised? Commented May 20, 2015 at 5:35
  • I wanted to know it there is a more direct way of indexing the list than elements[ i[0]] [i[1]] Commented May 20, 2015 at 5:37
  • FYI: Tuples and Sequences Commented May 20, 2015 at 5:39

7 Answers 7

9

If you really want to use tuples for indexing you can implement your own class that extends list and redefines __getattr__ to work with tuples and use that:

class TList(list):
    def __getitem__(self, index):
        if hasattr(index, "__iter__"):
            # index is list-like, traverse downwards
            item = self
            for i in index:
                item = item[i]
            return item
        # index is not list-like, let list.__getitem__ handle it
        return super().__getitem__(index)

elements = TList([ [ 'a', 'b', 'c'],
                   [ 'c', 'd', 'e'],
                   [ 'f', 'g', 'h'] ])
index = ( (0,0), (0,2), (2,0), (2,2) )
for i in index:
    print(elements[i])

a
c
f
h

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! That's exactly what I wanted. But in my case, as with propably most people, the extra effort to make it work is not really worth it.
2

Yes, you can do that. I wrote a similar example:

index = [ [0,0], [0,2], [2,0], [2,2] ]

elements = [ [ 'a', 'b', 'c'],
             [ 'c', 'd', 'e'],
             [ 'f', 'g', 'h'] ]

for i,j in index:
    print (elements [ i ] [ j ])

a c f h

4 Comments

This is pretty much identical to the OP's example; except you've swapped out i[0] and i[1] for i, j tuple unpacking! :) +1
Wait a sec, those square brackets do create lists and no tuples, don't they?
Thank you for the fast response.I just tested and it works. It's not as simple as the syntax that I wanted but it's great anyway. Getting rid of the extra brackets makes it look more clean. I will mark as answered as soon as I can
The downside to this is that you have to know the exact dimensions.
1

Here are some answers that work without knowing the dimension in advance.

A recursive version:

def multiget_rec(arr, *indices):
    if len(indices)==0:
        return arr
    return multiget_rec(arr[indices[0]], *indices[1:])

A procedural version:

def multiget_proc(arr, *indices):
    while len(indices)>0:
        i, *indices = indices
        arr = arr[i]
    return arr

And a version based on reduce which could be used as a 1-liner:

from functools import reduce
def multiget_reduce(arr, *indices):
    return reduce(lambda a,i:a[i], indices, arr)

All can be called like

for i in index:
    print (multiget(elements, *i))

Edit:

Using reduce seems to be the fastest of the three methods, at least for small arrays. I also think it looks the cleanest.

$ python -m timeit -s "def multiget_rec(arr, *indices):" \
    -s "    if len(indices)==0: return arr" \
    -s "    return multiget_rec(arr[indices[0]], *indices[1:])" \
    -- "d = [[[3]]]" \
    "multiget_rec(d, 0,0,0)"
500000 loops, best of 5: 941 nsec per loop
$ python -m timeit -s "def multiget_proc(arr, *indices):" \
    -s "    while len(indices)>0:" \
    -s "        i, *indices = indices" \
    -s "    return arr" \
    -s "d = [[[3]]]" \
    -- "multiget_proc(d, 0,0,0)"
500000 loops, best of 5: 529 nsec per loop
$ python -m timeit -s "from functools import reduce" \
    -s "def multiget_reduce(arr, *indices):" \
    -s "    return reduce(lambda a,i:a[i], indices, arr)" \
    -s "d = [[[3]]]" \
    -- "multiget_reduce(d, 0,0,0)"
1000000 loops, best of 5: 384 nsec per loop

Comments

0
# I would like to do this:
# print(elements[i])

No you cannot index a specific value of a nested list in this way.

The only slightly better way would be to "unpack" the tuples are you're iterating over them:

Example:

for i, j in index:
    print(elements[i][j])

See: Tuples ans Sequences

Comments

0

If you want to print everything in elements

index = ( (0,0), (0,2), (2,0), (2,2) )

elements = [ [ 'a', 'b', 'c'],
             [ 'c', 'd', 'e'],
             [ 'f', 'g', 'h'] ]

for row in elements:
    for i in range(len(row)):
        print (row[i])

1 Comment

Thanks! But I just wanted to visit some positions. That's why I wanted to save the indexes. I will need those positions more frequently than the rest of the matrix.
0

You can use list comprehensions:

index = ((0, 0), (0, 2), (2, 0), (2, 2))

elements = [['a', 'b', 'c'],
            ['c', 'd', 'e'],
            ['f', 'g', 'h']]

tmp = [print(elements[i][j]) for i,j in index]

1 Comment

You are still acessing it with elements[i[0]][i[1]]. But I think that you could unpack the tuple and do it like this: tmp = [print(elements[i][j]) for i,j in index]
-1

I know this question is 6 years ago but my solution might still be useful. (or not)

Solution:

the_func = lambda func, index1, index2: func[index1][index2]

func = [[1,2,3,4],[5,6,7,8]]
index = [(1, 1), (0, 1)] # [6, 2]
print(the_func(func, *index[0]))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.