在Python列表中查找某个值的第一个和最后一个索引


79

列表中是否包含任何内置方法,这些方法可以为我提供某些值的第一个和最后一个索引,例如:

verts.IndexOf(12.345)
verts.LastIndexOf(12.345)

1
也许您应该阅读Python文档中的列表:docs.python.org/library/stdtypes.html#mutable-sequence-types。我认为这比问很多问题要快得多。

2
我也在阅读它们,但是对python一无所知使事情变得更加困难。我发现它的语法很神秘。
Joan Venge

如果您的列表已排序,那么您可能需要检出bisect模块< docs.python.org/3/library/bisect.html >。
tommy.carstensen

见Dikei辉煌的答案在这里使用内置函数枚举和扭转:stackoverflow.com/questions/9836425/...
tommy.carstensen

Answers:


118

序列具有一种index(value)返回首次出现的索引的方法-在您的情况下为verts.index(value)

您可以对其运行verts[::-1]以找出最后一个索引。在这里,这将是len(verts) - 1 - verts[::-1].index(value)


谢谢,您如何执行列表[::-1]?verts [::-1]?
Joan Venge

2
Btw verts [::-1]只是反转列表,对不对?所以我必须补偿指数,对吗?
Joan Venge

12
肯定的是,这将是:LEN(绿党) - 1 -绿党[:: - 1]的.index(值)
SilentGhost

18

如果要搜索myvaluein中最后一次出现的索引mylist

len(mylist) - mylist[::-1].index(myvalue) - 1

这将抛出一个ValueError如果myvalue不存在mylist
尤金·雅玛什

10

作为小助手功能:

def rindex(mylist, myvalue):
    return len(mylist) - mylist[::-1].index(myvalue) - 1

5

Python列表具有该index()方法,您可以使用该方法查找列表中第一次出现的项的位置。请注意,如果该项目不在列表中,则会list.index()引发ValueError,因此您可能需要将其包装在try/中except

try:
    idx = lst.index(value)
except ValueError:
    idx = None

要有效地找到一个项目在列表中最后一次出现的位置(即不创建反向中间列表),可以使用以下功能:

def rindex(lst, value):
    for i, v in enumerate(reversed(lst)):
        if v == value:
            return len(lst) - i - 1  # return the index in the original list
    return None    

print(rindex([1, 2, 3], 3))     # 2
print(rindex([3, 2, 1, 3], 3))  # 3
print(rindex([3, 2, 1, 3], 4))  # None

5

也许找到最后一个索引的两种最有效方法:

def rindex(lst, value):
    lst.reverse()
    i = lst.index(value)
    lst.reverse()
    return len(lst) - i - 1
def rindex(lst, value):
    return len(lst) - operator.indexOf(reversed(lst), value) - 1

两者都仅占用O(1)额外空间,并且第一种解决方案的两次就地反转比创建反向副本要快得多。让我们将其与之前发布的其他解决方案进行比较:

def rindex(lst, value):
    return len(lst) - lst[::-1].index(value) - 1

def rindex(lst, value):
    return len(lst) - next(i for i, val in enumerate(reversed(lst)) if val == value) - 1

基准测试结果,我的解决方案是红色和绿色的解决方案: 整装待发

这是用于搜索一百万个数字列表中的数字。x轴用于搜索元素的位置:0%表示它在列表的开头,100%表示在列表的结尾。所有解决方案在位置100%处都是最快的,这两个reversed解决方案几乎不需要时间,双反向解决方案只需要一点时间,而反向复制则需要很多时间。

仔细看一下右端: 未改组,尾部

在位置100%处,反向复制解决方案和双向反向解决方案将所有时间都花在反转上(index()是即时的),因此我们看到两次就地反转的速度大约是创建反向副本的七倍。

以上是with的lst = list(range(1_000_000, 2_000_001)),它几乎可以在内存中顺序创建int对象,这对缓存非常友好。在对列表进行改组后,让我们再做一次random.shuffle(lst)(可能不太现实,但很有趣):

洗牌清单,全系列

随机排列的列表,尾部

一切都变慢了,正如预期的那样。反向复制解决方案遭受的损失最大,现在,与双重反向解决方案相比,反向复制解决方案遭受的损失高达100%约32倍。enumerate现在,-solution仅在定位98%之后排名第二。

总体而言,我operator.indexOf最喜欢该解决方案,因为它是所有位置的后半部分或四分之一位置中最快的一种,如果您实际上正在rindex做某事,那么这可能是更有趣的位置。而且它仅比早期位置的双反向解决方案慢一点。

Windows 10 Pro 1903 64位上使用CPython 3.9.0 64位完成的所有基准测试。


0

这种方法可以比上面更优化

def rindex(iterable, value):
    try:
        return len(iterable) - next(i for i, val in enumerate(reversed(iterable)) if val == value) - 1
    except StopIteration:
        raise ValueError

4
从节省空间的角度考虑,“经过优化”,但速度要慢50%。
dubiousjim

0
s.index(x[, i[, j]])

x在s中第一次出现的索引(在索引i或之后,在索引j之前)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.