How to find the last occurrence of an item in a Python list
Say I have this list:
li = ["a", "b", "a", "c", "x", "d", "a", "6"]
As far as help showed me, there is not a builtin function that returns the last occurrence of a string (like the reverse of index
). S开发者_开发技巧o basically, how can I find the last occurrence of "a"
in the given list?
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')
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.
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
>>> (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
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
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
last_occurence=len(yourlist)-yourlist[::-1].index(element)-1
just easy as that.no need to import or create a function.
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.
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)
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))
lastIndexOf = lambda array, item: len(array) - (array[::-1].index(item)) - 1
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')
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]
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')
精彩评论