How to find the last occurrence of an item in a Python list

Question:

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). So basically, how can I find the last occurrence of "a" in the given list?

Asked By: Shaokan

||

Answers:

>>> (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')
Answered By: wim

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
Answered By: senderle

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
Answered By: Isaac

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.

Answered By: alcalde

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))
Answered By: Laurent LAPORTE
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')
Answered By: sam-6174

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.

Answered By: dubiousjim

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)

Answered By: user2782561

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]
Answered By: quazgar
last_occurence=len(yourlist)-yourlist[::-1].index(element)-1

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

Answered By: Prabu M

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
Answered By: piRSquared
lastIndexOf = lambda array, item: len(array) - (array[::-1].index(item)) - 1
Answered By: Sapphire_Brick

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')
Answered By: danijar

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)
Answered By: Ilarion Halushka
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.