>>> (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:
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:
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.
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.
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.