J. Davis
J. Davis

Reputation: 113

What is the most efficient way to find the index of an element in a list, given only an element of a sublist (Python)

I.e. Does something like the following exist?

lst = [["a", "b", "c"], [4,5,6],"test"]
print getIndex(lst, "a")
>>> 0
print getIndex(lst, 5)
>>> 1
print getIndex(lst, "test")
>>> 2

I know of the regular index() method but that only looks for the immediate elements. I have a rough solution of making a new list, parsing through the superlist and adding "y" or "n" then looking for the index of the "y" in that one, but I feel there is much better way. Thanks

Upvotes: 1

Views: 186

Answers (4)

hellpanderr
hellpanderr

Reputation: 5896

def getIndex(lst,item):
    for n,i in enumerate(lst):
        if (type(i) == list and item in i) or i == item
            return n
getIndex(lst,'test')
>>> 2

Upvotes: 0

Jonathan Eunice
Jonathan Eunice

Reputation: 22443

There is a problem with hellpanderrr's solution. It assumes that the main list elements will only be lists or strings. It fails if one searches on a list where another type is in the main list (the in operation raises a TypeError). E.g.:

lst2 = [["a", "b", "c"], [4, 5, 6], "test", 19]


>>> getIndex(lst2, 19)
# Ugly TypeError stack trace ensues

To fix this:

def getIndex2(lst, item):
    for n, i in enumerate(lst):
        try:
            if item == i or item in i:
                return n
        except TypeError:
            pass
    return None

Now:

>>> getIndex2(lst2, "test")
2
>>> getIndex2(lst2, 19)
3

There are several ways to accomplish the "equals or in" test. This solution bowls right through, using a "get forgiveness not permission" idiom to catch the times when the in on i is not type-appropriate. It would also be possible to test the type of i before the in operation, or directly ask if i supports the in operation. But direct type inspection is often frowned upon, and strings and containers in Python have some complex overlapping capabilities. The "get forgiveness" approach gracefully handles those more simply.

Note that this also explicitly handles the case where no value is found.

>>> print getIndex2(lst2, 333)
None

While functions not returning a value implicitly return None, it is better to be explicit about such default cases.

By the by, this approach handles two levels. If the lists can be arbitrarily nested, a different approach, likely involving recursion, would be needed.

Upvotes: 2

alex
alex

Reputation: 94

Try using the default function to the list: list.index

l = [[1,2,3], ['a', 'b', 'c']]

l[0].index(2)  # index 1
l[1].index('b') # index 1

This generates a "ValueError" if the item does not exist.

Upvotes: 0

eqzx
eqzx

Reputation: 5559

use a generator

e.g. in >= Python 2.6, if you know the item exists in a sublist:

idx = next(i for i,v in enumerate(lst) if item in v)

Upvotes: 1

Related Questions