Skip to main content
corrected typo
Source Link
cottontail
  • 19.5k
  • 22
  • 111
  • 98
my_list = [1, 2, 3, 4]
target = 2.5

for item in my_list:
    if item > target:
        idx = lstmy_list.index(item)
        break

or as a one-liner:

idx = next(my_list.index(item) for item in my_list if item > target)
my_list = [1, 2, 3, 4]
target = 2.5

for item in my_list:
    if item > target:
        idx = lst.index(item)
        break
my_list = [1, 2, 3, 4]
target = 2.5

for item in my_list:
    if item > target:
        idx = my_list.index(item)
        break

or as a one-liner:

idx = next(my_list.index(item) for item in my_list if item > target)
fixed second benchmark, now the found index is always near the end
Source Link
cottontail
  • 19.5k
  • 22
  • 111
  • 98

In fact, in certain cases, it's much faster than enumerate() option that produces the same output. For example, especially if you need only 20% of the indices, list_index() used in this waylist is ~40% faster than enumerate()long.1

Another common exercise where index is often needed in a loop is to find the index of the first item in a list that issatisfies some condition (e.g. 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 that won't be used as in enumerate() (which will be unused) has a cost (which list.index() doesn't incur).

A timeit test shows that using list.index() as above in a for-loop is over 2 times faster than using enumerate().2benchmark

1 Get indices Code used to produce the plot of a target value.runtime speed ratios:

import random
import timeitmatplotlib.pyplot as plt
import perfplot


def enumerate_enumerate_1(lst, targettarget=3):
    
    result =return [i for i, v in enumerate(lst) if v == target]
    return result


def list_indexlist_index_1(lst, targettarget=3):
    
    result = []
    pos = -1
    while True:
        try:
            pos = lst.index(target, pos+1)
            result.append(pos)
        except ValueError:
            break
    return result


def list_index_2(lst, target):
    for item in lst:
    return result


random.seed(2023)
my_list = random.sample([0, 1,if 2,item 3,> 4,target:
 5]*10000, k=60000)
target = 3

# check that both produce the same output return lst.index(item)
assert

def list_indexenumerate_2(my_listlst, target):
 == enumerate_  return next(my_listi for i, item in enumerate(lst) if item > target)


t1setups = min(timeit.repeat(lambda[lambda n: list_index[random.randint(my_list1, target10), number=1000))
t2for =_ min(timeit.repeat(lambda:in enumerate_range(my_list, targetn)], number=1000))

print(t2 / t1)   # 1.43527199335277

2 Get index where a value exceeds a target value for the first time.

def list_index   lambda n: (lstlist(range(n)), targetn-1.5):]
kernels_list = [[enumerate_1, list_index_1], [enumerate_2, list_index_2]]
titles = ['Get indices forof itema invalue', lst:
'Get index that satisfies a condition']
n_range = [2**k iffor itemk >in target:range(1,21)]
    labels = ['enumerate', 'list.index']
xlabel = 'list length'

fig, axs return= lstplt.indexsubplots(item)


def1, enumerate_2, figsize=(lst10, target5):
  , facecolor='white', dpi=60)
   for returni, next(iax, forsu, iks, itemt) in enumerate(lst) ifzip(axs, itemsetups, >kernels_list, targettitles)

 ):
random    plt.seedsca(2023ax)
my_list = random  perfplot.sample(rangeplot(1000000)ks, k=1000000)
targetn_range, =su, random.randint(0None, 999999labels, xlabel, t, relative_to=1)

 
t1 = min  ax.xaxis.set_tick_params(timeitlabelsize=13)
plt.repeatsetp(lambda:axs, list_indexylim=(my_list0.7, target2.4), number=1000))
t2yticks=[i*0.25 =+ min(timeit0.repeat(lambda:75 enumerate_(my_list,for targeti in range(7)], number=1000))

print(t2 / t1       xlim=(1, 1100000), xscale='log', xticks=[1, #100, 210000, 1000000])
fig.6918576747465273tight_layout();

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

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).

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

In fact, in certain cases, it's much faster than enumerate() option that produces the same output especially if the list is long.

Another common exercise where index is often needed in a loop is to find the index of the first item in a list that satisfies some condition (e.g. 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 indices that won't be used as in enumerate() has a cost (which list.index() doesn't incur).

benchmark

Code used to produce the plot of runtime speed ratios:

import random
import matplotlib.pyplot as plt
import perfplot


def enumerate_1(lst, target=3):
    return [i for i, v in enumerate(lst) if v == target]


def list_index_1(lst, target=3):
    result = []
    pos = -1
    while True:
        try:
            pos = lst.index(target, pos+1)
            result.append(pos)
        except ValueError:
            break
    return result


def list_index_2(lst, target):
    for item in lst:
        if item > target:
            return lst.index(item)


def enumerate_2(lst, target):
    return next(i for i, item in enumerate(lst) if item > target)


setups = [lambda n: [random.randint(1, 10) for _ in range(n)], 
          lambda n: (list(range(n)), n-1.5)]
kernels_list = [[enumerate_1, list_index_1], [enumerate_2, list_index_2]]
titles = ['Get indices of a value', 'Get index that satisfies a condition']
n_range = [2**k for k in range(1,21)]
labels = ['enumerate', 'list.index']
xlabel = 'list length'

fig, axs = plt.subplots(1, 2, figsize=(10, 5), facecolor='white', dpi=60)
for i, (ax, su, ks, t) in enumerate(zip(axs, setups, kernels_list, titles)):
    plt.sca(ax)
    perfplot.plot(ks, n_range, su, None, labels, xlabel, t, relative_to=1)
    ax.xaxis.set_tick_params(labelsize=13)
plt.setp(axs, ylim=(0.7, 2.4), yticks=[i*0.25 + 0.75 for i in range(7)], 
         xlim=(1, 1100000), xscale='log', xticks=[1, 100, 10000, 1000000])
fig.tight_layout();
Source Link
cottontail
  • 19.5k
  • 22
  • 111
  • 98

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