Kludge
Kludge

Reputation: 2835

Transform tuple into indices?

It seems trivial on first look, yet surprisingly I haven't managed to find any answer online - consider the following list:

my_list = [[1,2,3], [4,5,6], [7,8,9]]

In order to get 5, I can go my_list[1][1], by 5's position of course.

Suppose I have the following tuple:

t = (1, 1)

Can I use this tuple in order to get 5 in a more appealing way then my_list[t[0]][t[1]] ?

Edit: please also take assignment into account, i.e more appealing then

my_list[t[0]][t[1]] = value

and final elements which are not necessarily numeric, e.g [['a', 'b'], ['c', 'd']]

Upvotes: 2

Views: 2036

Answers (5)

wwii
wwii

Reputation: 23753

You could create operator.itemgetter objects and apply them successively.

Example for two levels of nesting:

import operator
a = [[1,2,3], [4,5,6], [7,8,9]]
t = (1,2)
i1, i2 = map(operator.itemgetter, t)

>>> i2(i1(a))
6

Example for n levels of nesting (you have to ensure the target structure can accommodate the length of the tuple, maybe wrap it in a try/except):

a = [[[1,2,3], [4,5,6]], [[7,8,9],['a','b','c']]]
t = (0,1,0) # should produce 4
z = a[:]
for op_ig in map(operator.itemgetter, t):
    z = op_ig(z)

>>> z
4

To go a bit further using a couple of functions from functionally

from functionally import compose, thrush
a = [[[1,2,3], [4,5,6]], [[7,8,9],['a','b','c']]]
t = (1,1,0) # should produce a
foo = compose(*map(operator.itemgetter, reversed(t)))

>>> foo(a)
'a'
>>> thrush(a, *(operator.itemgetter(i) for i in t))
'a'

Upvotes: 0

Jason Hu
Jason Hu

Reputation: 6333

use reduce:

result = reduce(lambda acc, i: acc[i], t, my_list)

demo:

>>> my_list = [[1,2,3], [4,5,6], [7,8,9]]
>>> t = (1, 1)
>>> reduce(lambda acc, i: acc[i], t, my_list)
5

UPDATE

if an update is required:

reduce(lambda acc, i: acc[i], t[:-1], my_list)[t[-1]]

returns a "left value"(if i may say so). but it becomes ugly then, since the consistency of the expression is broken. and really, it can't wrapped as a function either.

i would suggest to write a simple wrapper like this:

class mdlist(list):
    def __getitem__(self, item):
        if isinstance(item, tuple):
            assert len(item) >= 1
            return self[item[0]][item[1:]] if len(item) > 1 else self[item[0]]
        else:
            return super(mdlist, self).__getitem__(item)

    def __setitem__(self, item, value):
        if isinstance(item, tuple):
            assert len(item) >= 1
            if len(item) > 1:
                self[item[0]][item[1:]] = value
            else:
                self[item[0]] = value
        else:
            super(mdlist, self).__setitem__(item, value)

it doesn't take more than you think and ease your life a lot.

Upvotes: 3

meelo
meelo

Reputation: 582

I think NumPy package just fit what you need. First turn the list into a NumPy array, then you can select a item by its position tuple.

import numpy as np

my_list = [[1,2,3], [4,5,6], [7,8,9]]
my_array = np.array(my_list)
t = (1, 1)
print my_array[t]

Upvotes: 2

jatinderjit
jatinderjit

Reputation: 1399

You can write a function to do that:

def nested_index(l, t):
    e = l
    for i in t:
        e = e[i]
    return e

Then you can call the function as:

nested_index(my_list, t)

Upvotes: 1

letsc
letsc

Reputation: 2567

You could use tuple unpacking to get your tuple values ( and ultimately you r list indices into variables )

>>> one , two = t
>>> mylist[one][two]
5

Upvotes: 1

Related Questions