BatWannaBe
BatWannaBe

Reputation: 4510

Using variable tuple to access elements of list

Disclaimer:beginner, self-teaching Python user.

A pretty cool feature of ndarrays is their ability to accept a tuple of integers as indices (e.g. myNDArray[(1,2)] == myNDArray[1][2]). This allows me to leave the indices unspecified as a variable (e.g. indicesTuple ) until a script determines what part of an ndarray to work with, in which case the variable is specified as a tuple of integers and used to access part of an ndarray (e.g. myNDArray[indicesTuple]). The utility in using a variable is that the LENGTH of the tuple can be varied depending on the dimensions of the ndarray.

However, this limits me to working with arrays of numerical values. I tried using lists, but they can't take in a tuple as indices (e.g. myList[(1,2)] gives an error.). Is there a way to "unwrap" a tuple for list indices as one could for function arguments? Or something far easier or more efficient?

UPDATE: Holy shite I forgot this existed. Basically I eventually learned that you can initialize the ndarray with the argument dtype=object, which allows the ndarray to contain multiple types of Python objects, much like a list. As for accessing a list, as a commenter pointed out, I could use a for-loop to iterate through the variable indicesTuple to access increasingly nested elements of the list. For in-place editing, see the accepted comment, really went the extra mile there.

Upvotes: 0

Views: 3094

Answers (4)

Kevin
Kevin

Reputation: 76194

I'm interpreting your question as:

I have an N-dimensional list, and a tuple containing N values (T1, T2... TN). How can I use the tuple values to access the list? I don't know what N will be ahead of time.

I don't know of a built-in way to do this, but you can write a method that iteratively digs into the list until you reach the innermost value.

def get(seq, indices):
    for index in indices:
        seq = seq[index]
    return seq

seq = [
    [
        ["a","b"],
        ["c","d"]
    ],
    [
        ["e","f"],
        ["g","h"]
    ]
]

indices = [0,1,0]
print get(seq, indices)

Result:

c

You could also do this in one* line with reduce, although it won't be very clear to the reader what you're trying to accomplish.

print reduce(lambda s, idx: s[idx], indices, seq)

(*if you're using 3.X, you'll need to import reduce from functools. So, two lines.)


If you want to set values in the N-dimensional list, use get to access the second-deepest level of the list, and assign to that.

def set(seq, indices, value):
    innermost_list = get(seq, indices[:-1])
    innermost_list[indices[-1]] = value

Upvotes: 1

Weeble
Weeble

Reputation: 17920

Use dictionaries indexed by tuple

>>> width, height = 7, 6
>>> grid = dict(
        ((x,y),"x={} y={}".format(x,y))
        for x in range(width)
        for y in range(height))
>>> print grid[3,1]
x=3 y=1

Use lists of lists

>>> width, height = 7, 6
>>> grid = [
        ["x={} y={}".format(x,y) for x in range(width)]
        for y in range(width)]
>>> print grid[1][3]
x=3 y=1

In this case, you could make a getter and setter function:

def get_grid(grid, index):
    x, y = index
    return grid[y][x]

def set_grid(grid, index, value):
    x, y = index
    grid[y][x] = value

You could go a step further and create your own class that contains a list of lists and defines an indexer that takes tuples as indexes and does this same process. It can do slightly more sensible bounds-checking and give better diagnostics than the dictionary, but it takes a bit of setup. I think the dictionary approach is fine for quick exploration.

Upvotes: 0

chepner
chepner

Reputation: 531275

Python doesn't have multidimensional lists, so myList[(1,2)] could only conceivably be considered a shortcut for (myList[1], myList[2]) (which would be pretty convenient sometimes, although you can use import operator; x = operator.itemgetter(1,2)(myList) to accomplish the same).

If your myList looks something like

myList = [ ["foo", "bar", "baz"], ["a", "b", c" ] ]

then myList[(1,2)] won't work (or make sense) because myList is not a two-dimensional list: it's a list that contains references to lists. You use myList[1][2] because the first index myList[1] returns the references to ["a", "b", "c"], to which you apply the second index [2] to get "c".

Slightly related, you could use a dictionary to simulate a sparse array precisely by using tuples as keys to a default dict.

import collections
d = collections.defaultdict(str)
d[(1,2)] = "foo"
d[(4,5)] = "bar"

Any other tuple you try to use as a key would return the empty string. It's not a perfect simulation, as you can't access full rows or columns of the array without using something like

row1 = [d[1, x] for x in range(C)] # where C is the number of columns
col3 = [d[x, 3] for x in range(R)] # where R is the number of columns

Upvotes: 0

Cory Kramer
Cory Kramer

Reputation: 117886

Say you have a list of (i,j) indexes

indexList = [(1,1), (0,1), (1,2)]

And some 2D list you want to index from

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

You could get those elements using a list comprehension as follows

>>> [l[i][j] for i,j in indexList]
[5, 2, 6]

Then your indexes can be whatever you want them to be. They will be unpacked in the list comprehension, and used as list indices. For your specific application, we'd have to see where your index variables were coming from, but that's the general idea.

Upvotes: 0

Related Questions