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

假设我有这个清单:

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

据我所知,没有一个内置函数返回字符串的最后一个匹配项(如 index的反向)。那么基本上,我怎样才能在给定的列表中找到 "a"的最后一个匹配项呢?

117684 次浏览
>>> (x for x in reversed(list(enumerate(li))) if x[1] == 'a').next()[0]
6


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

If you are actually using just single letters like shown in your example, then str.rindex would work handily. This raises a ValueError if there is no such item, the same error class as list.index would raise. Demo:

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

For the more general case you could use list.index on the reversed list:

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

The slicing here creates a copy of the entire list. That's fine for short lists, but for the case where li is very large, efficiency can be better with a lazy approach:

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

One-liner version:

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

I like both wim's and Ignacio's answers. However, I think itertools provides a slightly more readable alternative, lambda notwithstanding. (For Python 3; for Python 2, use xrange instead of range).

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

This will raise a StopIteration exception if the item isn't found; you could catch that and raise a ValueError instead, to make this behave just like index.

Defined as a function, avoiding the lambda shortcut:

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

It works for non-chars too. Tested:

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

Many of the other solutions require iterating over the entire list. This does not.

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

Edit: In hindsight this seems like unnecessary wizardry. I'd do something like this instead:

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

A one-liner that's like Ignacio's except a little simpler/clearer would be

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

It seems very clear and Pythonic to me: you're looking for the highest index that contains a matching value. No nexts, lambdas, reverseds or itertools required.

Use a simple loop:

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))
def rindex(lst, val):
try:
return next(
len(lst) - n
for n, v in enumerate(reversed(lst), start=1)
if v == val
)
except StopIteration:
raise ValueError(f'{val} is not in list')

I came here hoping to find someone had already done the work of writing the most efficient version of list.rindex, which provided the full interface of list.index (including optional start and stop parameters). I didn't find that in the answers to this question, or here, or here, or here. So I put this together myself... making use of suggestions from other answers to this and the other questions.

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)

The technique using len(seq) - 1 - next(i for i,v in enumerate(reversed(seq)) if v == value), suggested in several other answers, can be more space-efficient: it needn't create a reversed copy of the full list. But in my (offhand, casual) testing, it's about 50% slower.

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

If you need to find last occurence of 2

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

Here's a little one-liner for obtaining the last index, using enumerate and a list comprehension:

li = ["a", "b", "a", "c", "x", "d", "a", "6"]
[l[0] for l in enumerate(li) if l[1] == "a"][-1]
last_occurence=len(yourlist)-yourlist[::-1].index(element)-1

just easy as that.no need to import or create a function.

With dict

You can use the fact that dictionary keys are unique and when building one with tuples only the last assignment of a value for a particular key will be used. As stated in other answers, this is fine for small lists but it creates a dictionary for all unique values and might not be efficient for large lists.

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


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

If the list is small, you can compute all indices and return the largest:

index = max(i for i, x in enumerate(elements) if x == 'foo')

Love @alcalde's solution, but faced ValueError: max() arg is an empty sequence if none of the elements match the condition.

To avoid the error set default=None:

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