>>> ["foo", "bar", "baz"].index("bar")
1
See the documentation for the built-in .index() method of the list:
list.index(x[, start[, end]])Return zero-based index in the list of the first item whose value is equal to x. Raises a
ValueErrorif there is no such item.The optional arguments start and end are interpreted as in the slice notation and are used to limit the search to a particular subsequence of the list. The returned index is computed relative to the beginning of the full sequence rather than the start argument.
Caveats
Linear time-complexity in list length
An index call checks every element of the list in order, until it finds a match. If the list is long, and if there is no guarantee that the value will be near the beginning, this can slow down the code.
This problem can only be completely avoided by using a different data structure. However, if the element is known to be within a certain part of the list, the start and end parameters can be used to narrow the search.
For example:
>>> import timeit
>>> timeit.timeit('l.index(999_999)', setup='l = list(range(0, 1_000_000))', number=1000)
9.356267921015387
>>> timeit.timeit('l.index(999_999, 999_990, 1_000_000)', setup='l = list(range(0, 1_000_000))', number=1000)
0.0004404920036904514
The second call is orders of magnitude faster, because it only has to search through 10 elements, rather than all 1 million.
Only the index of the first match is returned
A call to index searches through the list in order until it finds a match, and stops there. If there could be more than one occurrence of the value, and all indices are needed, index cannot solve the problem:
>>> [1, 1].index(1) # the `1` index is not found.
0
Instead, use a list comprehension or generator expression to do the search, with enumerate to get indices:
>>> # A list comprehension gives a list of indices directly:
>>> [i for i, e in enumerate([1, 2, 1]) if e == 1]
[0, 2]
>>> # A generator comprehension gives us an iterable object...
>>> g = (i for i, e in enumerate([1, 2, 1]) if e == 1)
>>> # which can be used in a `for` loop, or manually iterated with `next`:
>>> next(g)
0
>>> next(g)
2
The list comprehension and generator expression techniques still work if there is only one match, and are more generalizable.
Raises an exception if there is no match
As noted in the documentation above, using .index will raise an exception if the searched-for value is not in the list:
>>> [1, 1].index(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 2 is not in list
If this is a concern, either explicitly check first using item in my_list, or handle the exception with try/except as appropriate.
The explicit check is simple and readable, but it must iterate the list a second time. See What is the EAFP principle in Python? for more guidance on this choice.
Answer from Alex Coventry on Stack Overflow>>> ["foo", "bar", "baz"].index("bar")
1
See the documentation for the built-in .index() method of the list:
list.index(x[, start[, end]])Return zero-based index in the list of the first item whose value is equal to x. Raises a
ValueErrorif there is no such item.The optional arguments start and end are interpreted as in the slice notation and are used to limit the search to a particular subsequence of the list. The returned index is computed relative to the beginning of the full sequence rather than the start argument.
Caveats
Linear time-complexity in list length
An index call checks every element of the list in order, until it finds a match. If the list is long, and if there is no guarantee that the value will be near the beginning, this can slow down the code.
This problem can only be completely avoided by using a different data structure. However, if the element is known to be within a certain part of the list, the start and end parameters can be used to narrow the search.
For example:
>>> import timeit
>>> timeit.timeit('l.index(999_999)', setup='l = list(range(0, 1_000_000))', number=1000)
9.356267921015387
>>> timeit.timeit('l.index(999_999, 999_990, 1_000_000)', setup='l = list(range(0, 1_000_000))', number=1000)
0.0004404920036904514
The second call is orders of magnitude faster, because it only has to search through 10 elements, rather than all 1 million.
Only the index of the first match is returned
A call to index searches through the list in order until it finds a match, and stops there. If there could be more than one occurrence of the value, and all indices are needed, index cannot solve the problem:
>>> [1, 1].index(1) # the `1` index is not found.
0
Instead, use a list comprehension or generator expression to do the search, with enumerate to get indices:
>>> # A list comprehension gives a list of indices directly:
>>> [i for i, e in enumerate([1, 2, 1]) if e == 1]
[0, 2]
>>> # A generator comprehension gives us an iterable object...
>>> g = (i for i, e in enumerate([1, 2, 1]) if e == 1)
>>> # which can be used in a `for` loop, or manually iterated with `next`:
>>> next(g)
0
>>> next(g)
2
The list comprehension and generator expression techniques still work if there is only one match, and are more generalizable.
Raises an exception if there is no match
As noted in the documentation above, using .index will raise an exception if the searched-for value is not in the list:
>>> [1, 1].index(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 2 is not in list
If this is a concern, either explicitly check first using item in my_list, or handle the exception with try/except as appropriate.
The explicit check is simple and readable, but it must iterate the list a second time. See What is the EAFP principle in Python? for more guidance on this choice.
The majority of answers explain how to find a single index, but their methods do not return multiple indexes if the item is in the list multiple times. Use enumerate():
for i, j in enumerate(['foo', 'bar', 'baz']):
if j == 'bar':
print(i)
The index() function only returns the first occurrence, while enumerate() returns all occurrences.
As a list comprehension:
[i for i, j in enumerate(['foo', 'bar', 'baz']) if j == 'bar']
Here's also another small solution with itertools.count() (which is pretty much the same approach as enumerate):
from itertools import izip as zip, count # izip for maximum efficiency
[i for i, j in zip(count(), ['foo', 'bar', 'baz']) if j == 'bar']
This is more efficient for larger lists than using enumerate():
$ python -m timeit -s "from itertools import izip as zip, count" "[i for i, j in zip(count(), ['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 174 usec per loop
$ python -m timeit "[i for i, j in enumerate(['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 196 usec per loop
Get index in the list of objects by attribute in Python - Stack Overflow
How to find the index of something in a list without knowing the full item
__getitem__ method in 2D array class.
How to delete from a deque in constant time without "pointers"?
There's a technique which I call 'lazy popping' which can help here.
The idea is that you don't delete immediately from the queue. Rather, you leave deleted items in the queue, but mark them as deleted in another data structure -- usually a set. Whenever you have to pop an item to execute, keep popping until you reach an item that hasn't yet been deleted.
This gives you constant-time push, amortized constant-time pop (although you may pop multiple deleted items off the queue each time you pop an item to execute, each item only gets popped exactly once) , and constant-time deletion, which is better than what you can get by maintaining a list and deleting from start or middle.
In this case, you'd save the IDs of deleted items in the set. It looks like this (untested code):
import collections
class DeletableQueue:
def __init__(self):
self.deleted = set()
self.queue = collections.deque()
def push(self, item):
self.queue.append(item)
def pop(self):
# Precondition: there is at least one non-deleted item on the queue.
while id(q[0]) in deleted:
q[0].pop_left() # Discard an already-deleted item.
return q.pop_left() # Return the actual item to pop
def delete(self, item_to_delete):
self.deleted.add(id(item_to_delete)) More on reddit.com Use enumerate when you want both the values and indices in a for loop:
for index, item in enumerate(my_list):
if item.id == 'specific_id':
break
else:
index = -1
Or, as a generator expression:
index = next((i for i, item in enumerate(my_list) if item.id == 'specific_id'), -1)
Here's an alternative that doesn't use an (explicit) loop, with two different approaches to generating the list of 'id' values from the original list.
try:
# index = map(operator.attrgetter('id'), my_list).index('specific_id')
index = [ x.id for x in my_list ].index('specific_id')
except ValueError:
index = -1