如何在Python列表中查找项目的最后一次出现


76

说我有这个清单:

li = ["a", "b", "a", "c", "x", "d", "a", "6"]

据帮助显示,没有内置函数返回字符串的最后一次出现(如的反向index)。因此,基本上,如何找到"a"给定列表中的最后一个出现?

Answers:


90

如果您实际上仅使用示例中显示的单个字母,则将str.rindex很方便。这就提出了一个ValueError如果没有这样的项目,相同的错误类作为list.index将提高。演示:

>>> li = ["a", "b", "a", "c", "x", "d", "a", "6"]
>>> ''.join(li).rindex('a')
6

对于更一般的情况,您可以list.index在反向列表中使用:

>>> len(li) - 1 - li[::-1].index('a')
6

此处的切片将创建整个列表的副本。简短列表就可以了,但是对于li很大的情况,使用惰性方法可以提高效率:

def list_rindex(li, x):
    for i in reversed(range(len(li))):
        if li[i] == x:
            return i
    raise ValueError("{} is not in list".format(x))

一线版:

next(i for i in reversed(range(len(li))) if li[i] == 'a')

2
YMMV,但对于本例来说,len(li) - next(i for i, v in enumerate(reversed(li), 1) if v == 'a')对我来说要快一些
John La Rooy

11
str.rindex(),有没有原因list.rindex()吗?
Chris_Rands

2
@Chris_Rands:比range(len(li)-1, -1, -1)reversed(range(len(li)))或”更好range(len(li))[::-1](它们也大致相等,不像大多数与reversed反向切片之间的比较;现代Py3range可以反向切片,以使另一个懒惰range向后运行,因此性能相同)。好像wim跟前者一起去了。
ShadowRanger

@ShadowRanger是的,自从我写评论以来,wim编辑了他的答案。这是当时stackoverflow.com/revisions/的答案。为了避免混淆,我将删除我之前的评论(和本评论)
Chris_Rands

Pythonic版本next(len(a)-i-1 for i, x in enumerate(reversed(a)) if x == 3)
coder.in.me

36

像伊格纳西奥(Ignacio)一样的单线飞机会更简单/更清晰

max(loc for loc, val in enumerate(li) if val == 'a')

在我看来,这似乎很清楚且具有Python风格:您正在寻找包含匹配值的最高索引。不需要下一步,lambda,reverses或itertools。


7
正如@Isaac指出的那样,它总是在li的所有N个元素上进行迭代。
smci 2014年

1
太棒了。对于大型数据集而言可能并不理想,但对于少量数据而言则非常理想。
摇摆了2013年

一个重大的缺点是您将始终遍历整个列表。您应该在回答中强调这一点。
Guyarad '18

17

许多其他解决方案都需要遍历整个列表。这不是。

def find_last(lst, elm):
  gen = (len(lst) - 1 - i for i, v in enumerate(reversed(lst)) if v == elm)
  return next(gen, None)

编辑:事后看来,这似乎是不必要的巫术。我会做这样的事情:

def find_last(lst, sought_elt):
    for r_idx, elt in enumerate(reversed(lst)):
        if elt == sought_elt:
            return len(lst) - 1 - r_idx

我怀疑区别是C循环与Python循环。
阿米特·奈杜

8
>>> (x for x in reversed([y for y in enumerate(li)]) if x[1] == 'a').next()[0]
6

>>> len(li) - (x for x in (y for y in enumerate(li[::-1])) if x[1] == 'a').next()[0] - 1
6

2
为什么不reversed(enumerate(li))呢?
以撒

31
因为3年前我没有发生过。
伊格纳西奥·巴斯克斯

但是,很奇怪,reversed(enumerate(li))导致读取错误argument to reversed() must be a sequence!它也说reversed((y for y in enumarete(li))
trss 2014年

2
reversed()通常不能在迭代器(包括生成器)上运行,这是有道理的。因此,enumerate(reversed(li))调整枚举元组的索引组件是一种避免创建列表副本的解决方法。
trss 2014年

7

我都喜欢 WIM的伊格纳西奥的答案。但是,itertools尽管有lambda,但我认为提供了一种更具可读性的替代方法。(对于Python 3;对于Python 2,请使用xrange代替range)。

>>> from itertools import dropwhile
>>> l = list('apples')
>>> l.index('p')
1
>>> next(dropwhile(lambda x: l[x] != 'p', reversed(range(len(l)))))
2

StopIteration如果找不到该项目,则会引发异常;您可以抓住它并提出一个ValueError替代方案,以使其表现得像index

定义为函数,避免了lambda快捷方式:

def rindex(lst, item):
    def index_ne(x):
        return lst[x] != item
    try:
        return next(dropwhile(index_ne, reversed(range(len(lst)))))
    except StopIteration:
        raise ValueError("rindex(lst, item): item not in list")

它也适用于非字符。经测试:

>>> rindex(['apples', 'oranges', 'bananas', 'apples'], 'apples')
3

Python 3现在允许使用默认参数arg next,因此您可以消除try... except
PM 2Ring

@ PM2Ring,我不知道为什么您要在这里使用默认的arg。然后,您需要if声明并提出一个ValueError要点。(回想一下,我们正在尝试index精确地复制API,这意味着ValueError如果找不到该项目,则会引发一个。)try在这种情况下使用会更加惯用,我想会更有效。
senderle

公平电话。也许要加薪,IndexError而不是ValueError与之保持一致.index
下午

IndexError用于传递错误的索引。传递缺失值以index生成ValueError
senderle

5

dict

您可以使用以下事实:字典键是唯一的,并且在使用元组构建字典键时,只会使用特定键的值的最后分配。如其他答案中所述,这对于小型列表很好,但是它为所有唯一值创建字典,对于大型列表可能不是有效的。

dict(map(reversed, enumerate(li)))["a"]

6

2

我来到这里的目的是希望发现有人已经完成了的最高效版本的编写工作,该版本list.rindex提供的完整接口list.index(包括可选startstop参数)。我没有发现,在回答这个问题,还是在这里,或这里,或这里。因此,我自己整理了这些信息...利用了有关此问题的其他答案以及其他问题的建议。

def rindex(seq, value, start=None, stop=None):
  """L.rindex(value, [start, [stop]]) -> integer -- return last index of value.
  Raises ValueError if the value is not present."""
  start, stop, _ = slice(start, stop).indices(len(seq))
  if stop == 0:
    # start = 0
    raise ValueError('{!r} is not in list'.format(value))
  else:
    stop -= 1
    start = None if start == 0 else start - 1
  return stop - seq[stop:start:-1].index(value)

len(seq) - 1 - next(i for i,v in enumerate(reversed(seq)) if v == value)在其他几个答案中建议使用的技术,可以节省更多空间:无需创建完整列表的反向副本。但是在我的(临时的,临时的)测试中,它慢了约50%。


0

使用一个简单的循环:

def reversed_index(items, value):
    for pos, curr in enumerate(reversed(items)):
        if curr == value:
            return len(items) - pos - 1
    raise ValueError("{0!r} is not in list".format(value))

0
last_occurence=len(yourlist)-yourlist[::-1].index(element)-1

就这么简单。无需导入或创建函数。


0
lastIndexOf = lambda array, item: len(array) - (array[::-1].index(item)) - 1

-1
def rindex(lst, val):
    try:
        return next(len(lst)-i for i, e in enumerate(reversed(lst), start=1) if e == val)
    except StopIteration:
        raise ValueError('{} is not in list'.format(val))

-1

val = [1,2,2,2,2,2,4,5]。

如果您需要找到2的最后一次出现

last_occurence = (len(val) -1) - list(reversed(val)).index(2)


-1

这是使用enumerate和列表理解来获取最后一个索引的简单方法:

li = ["a", "b", "a", "c", "x", "d", "a", "6"]
[l[0] for l in enumerate(li) if l[1] == "a"][-1]
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.