Как найти последнее вхождение элемента в список Python - PullRequest
61 голосов
/ 31 июля 2011

Скажите, у меня есть этот список:

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

Насколько мне показала справка, нет встроенной функции, которая возвращает последнее вхождение строки (подобно обратному index). В общем, как я могу найти последнее вхождение "a" в данном списке?

Ответы [ 13 ]

74 голосов
/ 31 июля 2011

Если вы на самом деле используете только отдельные буквы, как показано в вашем примере, то 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')
29 голосов
/ 23 мая 2014

Однострочный, похожий на Ignacio, за исключением немного более простого / понятного будет

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

Мне кажется, это очень ясно и на Pythonic: вы ищете самый высокий индекс, который содержит совпадающее значение.Нет необходимости использовать nexts, lambdas, reversed или itertools.

15 голосов
/ 18 апреля 2014

Многие другие решения требуют итерации по всему списку.Это не так.

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
7 голосов
/ 01 августа 2011

Мне нравятся ответы wim и Ignacio . Тем не менее, я думаю, что itertools обеспечивает немного более читабельную альтернативу, несмотря на лямбду. (Для 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
6 голосов
/ 31 июля 2011
>>> (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
4 голосов
/ 11 июня 2018

С dict

Вы можете использовать тот факт, что словарные ключи являются уникальными, и при создании ключа с кортежами будет использоваться только последнее присвоение значения для определенного ключа.Как указано в других ответах, это подходит для небольших списков, но создает словарь для всех уникальных значений и может быть неэффективным для больших списков.

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

6
2 голосов
/ 26 июня 2017

Я пришел сюда в надежде найти кого-то, кто уже выполнил работу по написанию самой эффективной версии list.rindex, которая обеспечивала полный интерфейс list.index (включая необязательные параметры start и stop).Я не нашел этого в ответах на этот вопрос, или здесь , или здесь , или здесь .Так что я собрал это воедино сам… используя предложения из других ответов на этот и другие вопросы.

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 голосов
/ 20 сентября 2018
from array import array
fooa = array('i', [1,2,3])
fooa.reverse()  # [3,2,1]
fooa.index(1)
>>> 2
0 голосов
/ 13 апреля 2018
last_occurence=len(yourlist)-yourlist[::-1].index(element)-1

просто, так как нет необходимости импортировать или создавать функцию.

0 голосов
/ 04 января 2018

Вот небольшая строка для получения последнего индекса, используя enumerate и понимание списка:

li = ["a", "b", "a", "c", "x", "d", "a", "6"]
[l[0] for l in enumerate(li) if l[1] == "a"][-1]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...