Finding the index of an item given a list containing it in Python
For a list
["foo", "bar", "baz"] and an item in the list
"bar", what's the cleanest way to get its index (1) in Python?
Well, sure, there's the index method, which returns the index of the first occurrence:
>>> l = ["foo", "bar", "baz"]
There are a couple of issues with this method:
- if the value isn't in the list, you'll get a
- if more than one of the value is in the list, you only get the index for the first one
If the value could be missing, you need to catch the
You can do so with a reusable definition like this:
def index(a_list, value):
And use it like this:
>>> print(index(l, 'quux'))
>>> print(index(l, 'bar'))
And the downside of this is that you will probably have a check for if the returned value
is not None:
result = index(a_list, value)
if result is not None:
More than one value in the list
If you could have more occurrences, you'll not get complete information with
['foo', 'bar', 'baz', 'bar']
>>> l.index('bar') # nothing at index 3?
You might enumerate into a list comprehension the indexes:
>>> [index for index, v in enumerate(l) if v == 'bar']
>>> [index for index, v in enumerate(l) if v == 'boink']
If you have no occurrences, you can check for that with boolean check of the result, or just do nothing if you loop over the results:
indexes = [index for index, v in enumerate(l) if v == 'boink']
for index in indexes:
Better data munging with pandas
If you have pandas, you can easily get this information with a Series object:
>>> import pandas as pd
>>> series = pd.Series(l)
A comparison check will return a series of booleans:
>>> series == 'bar'
Pass that series of booleans to the series via subscript notation, and you get just the matching members:
>>> series[series == 'bar']
If you want just the indexes, the index attribute returns a series of integers:
>>> series[series == 'bar'].index
Int64Index([1, 3], dtype='int64')
And if you want them in a list or tuple, just pass them to the constructor:
>>> list(series[series == 'bar'].index)
Yes, you could use a list comprehension with enumerate too, but that's just not as elegant, in my opinion - you're doing tests for equality in Python, instead of letting builtin code written in C handle it:
>>> [i for i, value in enumerate(l) if value == 'bar']
Is this an XY problem?
The XY problem is asking about your attempted solution rather than your actual problem.
Why do you think you need the index given an element in a list?
If you already know the value, why do you care where it is in a list?
If the value isn't there, catching the
ValueError is rather verbose - and I prefer to avoid that.
I'm usually iterating over the list anyways, so I'll usually keep a pointer to any interesting information, getting the index with enumerate.
If you're munging data, you should probably be using pandas - which has far more elegant tools than the pure Python workarounds I've shown.
I do not recall needing
list.index, myself. However, I have looked through the Python standard library, and I see some excellent uses for it.
There are many, many uses for it in
idlelib, for GUI and text parsing.
keyword module uses it to find comment markers in the module to automatically regenerate the list of keywords in it via metaprogramming.
In Lib/mailbox.py it seems to be using it like an ordered mapping:
key_list[key_list.index(old)] = new
In Lib/http/cookiejar.py, seems to be used to get the next month:
mon = MONTHS_LOWER.index(mon.lower())+1
In Lib/tarfile.py similar to distutils to get a slice up to an item:
members = members[:members.index(tarinfo)]
numtopop = before.index(markobject)
What these usages seem to have in common is that they seem to operate on lists of constrained sizes (important because of O(n) lookup time for
list.index), and they're mostly used in parsing (and UI in the case of Idle).
While there are use-cases for it, they are fairly uncommon. If you find yourself looking for this answer, ask yourself if what you're doing is the most direct usage of the tools provided by the language for your use-case.