4

I am trying to find the index of a letter in the following list of lists of lists:

For example:

>>> alphabet = [[["A","B","C"],["D","E","F"],["G","H","I"]],[["J","K","L"],["M","N","O"],["P","Q","R"]],[["S","T","U"],["V","W","X"],["Y","Z","_"]]]
>>> find("H",alphabet)
(0,2,1)

What is the most Pythonic way of doing this?

6
  • What alternative should I be using? Commented Jan 2, 2013 at 15:02
  • @HarryE-W -- That really depends on what you're actually using the lists for ... In your example, a flat list seems like the way to go: alphabet = ["A","B","C","D",...] Commented Jan 2, 2013 at 15:02
  • @mgilson - I am just using it as a way of storing the alphabet, but I need to retrieve the index of a letter in the format I outlined in the example above. I am effectively putting the alphabet into a 3x3x3 cube and retrieving the x,y, and z index. Commented Jan 2, 2013 at 15:04
  • 2
    @HarryE-W: wouldn't it be easier to calculate the address from the ordinal? H is the 8th letter, calculate it's position into the cube from there? Commented Jan 2, 2013 at 15:08
  • @HarryE-W -- In that case, you can probably do it analytically -- which is basically the same thing that Martijn beat me to suggesting by a couple seconds ... Commented Jan 2, 2013 at 15:08

3 Answers 3

5

If you really want a solution that deals with this to any depth, this is the kind of thing you are looking for (as a simple recursive function):

def find_recursive(needle, haystack):
    for index, item in enumerate(haystack):
        if not isinstance(item, str):
            try:
                path = find_recursive(needle, item)
                if path is not None:
                    return (index, ) + path
            except TypeError:
                pass
        if needle == item:
            return index,
    return None

Edit: Just remembered, in 2.x, you want basestring to allow for unicode strings too - this solution is fine for 3.x users.

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

Comments

4

You could simply change the data structure and use a dict:

>>> import itertools
>>> import string
>>> lets = string.ascii_uppercase
>>> where = dict(zip(lets, itertools.product(range(3), repeat=3)))
>>> where
{'A': (0, 0, 0), 'C': (0, 0, 2), 'B': (0, 0, 1), 'E': (0, 1, 1), 'D': (0, 1, 0), 'G': (0, 2, 0), 'F': (0, 1, 2), 'I': (0, 2, 2), 'H': (0, 2, 1), 'K': (1, 0, 1), 'J': (1, 0, 0), 'M': (1, 1, 0), 'L': (1, 0, 2), 'O': (1, 1, 2), 'N': (1, 1, 1), 'Q': (1, 2, 1), 'P': (1, 2, 0), 'S': (2, 0, 0), 'R': (1, 2, 2), 'U': (2, 0, 2), 'T': (2, 0, 1), 'W': (2, 1, 1), 'V': (2, 1, 0), 'Y': (2, 2, 0), 'X': (2, 1, 2), 'Z': (2, 2, 1)}
>>> where["H"]
(0, 2, 1)

but note that I don't double the locations of the U to pad, and so

>>> where["U"]
(2, 0, 2)

1 Comment

Nice and compact solution!
3
In [9]: def find(val,lis):
        ind=[(j,i,k) for j,x in enumerate(lis) for i,y in enumerate(x) \
                                             for k,z in enumerate(y) if z==val]
        return ind[0] if ind else None
   ...: 

In [10]: find("H",alphabet)
Out[10]: (0, 2, 1)

In [14]: find("M",alphabet)
Out[14]: (1, 1, 0)

1 Comment

Would be even better if you could write it to take care of an indefinite number of nestings.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.