Yet another way to run a counter in a for loop is to use itertools.count
.
from itertools import count
my_list = ['a', 'b', 'a']
for i, item in zip(count(), my_list):
print(i, item)
This is useful especially if you want the counters to be fractional numbers. In the following example, the "index" starts from 1.0 and is incremented by 0.5 in each iteration.
my_list = ['a', 'b', 'a']
for i, item in zip(count(start=1., step=0.5), my_list):
print(f"loc={i}, item={item}")
# loc=1.0, item=a
# loc=1.5, item=b
# loc=2.0, item=a
Another method is to use list.index()
inside a loop. However, in contrast to other answers on this page mentioning this method (1, 2, 3), the starting point of the index search (which is the second argument) must be passed to the list.index()
method. This lets you achieve 2 things: (1) Doesn't expensively loop over the list from the beginning again; (2) Can find the index of all values, even duplicates.
my_list = ['a', 'b', 'a']
idx = -1
for item in my_list:
idx = my_list.index(item, idx+1)
# ^^^^^ <---- start the search from the next index
print(f"index={idx}, item={item}")
# index=0, item=a
# index=1, item=b
# index=2, item=a
In terms of performance, if you want all/most of the indices, enumerate()
is the fastest option. If you were looking for only specific indices, then list.index()
may be more efficient. Below are two examples where list.index()
is more efficient.
Example #1: Index of specific values
Suppose you want to find all indices where a specific value appears in a list (e.g. highest value). For example, in the following case, we want to find all indices that 2 appears in. This is a one-liner using enumerate()
. However, we can also search for the indices of 2 by using the list.index()
method in a while-loop; as mentioned before, in each iteration, we start the index search from where we left off in the previous iteration.
lst = [0, 2, 1, 2]
target = 2
result = []
pos = -1
while True:
try:
pos = lst.index(target, pos+1)
result.append(pos)
except ValueError:
break
print(result) # [1, 3]
In fact, in certain cases, it's much faster than enumerate()
option that produces the same output. For example, if you need only 20% of the indices, list_index()
used in this way is ~40% faster than enumerate()
.1
Example #2: Index of the first number that is smaller than a target
Another common exercise where index is often needed in a loop is to find the index of the first item in a list that is greater/smaller than some target value. In the following example, we want to find the index of the first value that exceeds 2.5. This is a one-liner using enumerate()
but using list.index()
is more efficient because getting all the indices as in enumerate()
(which will be unused) has a cost (which list.index()
doesn't incur).
my_list = [1, 2, 3, 4]
target = 2.5
for item in my_list:
if item > target:
idx = lst.index(item)
break
A timeit test shows that using list.index()
as above in a for-loop is over 2 times faster than using enumerate()
.2
1 Get indices of a target value.
import random
import timeit
def enumerate_(lst, target):
result = [i for i, v in enumerate(lst) if v == target]
return result
def list_index(lst, target):
result = []
pos = -1
while True:
try:
pos = lst.index(target, pos+1)
result.append(pos)
except ValueError:
break
return result
random.seed(2023)
my_list = random.sample([0, 1, 2, 3, 4, 5]*10000, k=60000)
target = 3
# check that both produce the same output.
assert list_index(my_list, target) == enumerate_(my_list, target)
t1 = min(timeit.repeat(lambda: list_index(my_list, target), number=1000))
t2 = min(timeit.repeat(lambda: enumerate_(my_list, target), number=1000))
print(t2 / t1) # 1.43527199335277
2 Get index where a value exceeds a target value for the first time.
def list_index(lst, target):
for item in lst:
if item > target:
return lst.index(item)
def enumerate_(lst, target):
return next(i for i, item in enumerate(lst) if item > target)
random.seed(2023)
my_list = random.sample(range(1000000), k=1000000)
target = random.randint(0, 999999)
t1 = min(timeit.repeat(lambda: list_index(my_list, target), number=1000))
t2 = min(timeit.repeat(lambda: enumerate_(my_list, target), number=1000))
print(t2 / t1) # 2.6918576747465273