Python индекс последнего вхождения

Как найти последнее вхождение элемента в список Python

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

13 ответов

Если вы на самом деле используете только отдельные буквы, как показано в вашем примере, то str.rindex будет работать str.rindex . Это вызывает ValueError если такого элемента нет, тот же класс ошибок, что и list.index . Демо-версия:

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

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

def list_rindex(seq, x): for i in reversed(range(len(seq))): if seq[i] == x: return x raise ValueError("<> is not in list".format(x)) 
next(i for i in range(len(li)-1, -1, -1) if li[i] == 'a') 

YMMV, но для этого примера len(li) — next(i for i, v in enumerate(reversed(li), 1) if v == ‘a’) немного быстрее для меня

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

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

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

Это потрясающе. Может быть, не идеально для большого набора данных, но для небольшого объема данных это здорово.

Читайте также:  Измерение времени работы программы python

Существенным недостатком является тот факт, что вы всегда будете перебирать весь список. Вы должны подчеркнуть это в своем ответе.

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

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 

Мне нравятся ответы Вим и Игнасио. Тем не менее, я думаю, что 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 исключение 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 

Python 3 теперь допускает использование аргумента по умолчанию для next , так что вы можете исключить try. except

@PM2Ring PM2Ring, я не знаю, почему вы хотите использовать здесь аргумент по умолчанию. Тогда вам нужно иметь оператор if и вызвать внутри него ValueError . (Вспомните, что мы пытаемся точно продублировать index API, что означает повышение значения ValueError если элемент не может быть найден.) Использование try в этом контексте более идиоматично и, я думаю, более эффективно.

IndexError — для передачи неверного индекса. Передача пропущенного значения в index создает ValueError .

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

Как ни странно, reversed(enumerate(li)) приводит к ошибке, при которой argument to reversed() must be a sequence ! И это говорит, что для reversed((y for y in enumarete(li)) тоже!

Имеет смысл, что reversed() не может работать с итераторами вообще, включая генераторы. Таким образом, enumerate(reversed(li)) и настройка компонента индекса перечислимых кортежей — это обходной путь, позволяющий избежать создания копии списка.

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

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

Я пришел сюда, надеясь найти, что кто-то уже выполнил работу по написанию наиболее эффективной версии 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(' 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% медленнее.

Источник

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

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?

15 Answers 15

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:

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)) 
next(i for i in reversed(range(len(li))) if li[i] == 'a') 

@Chris_Rands: Better than range(len(li)-1, -1, -1) is either reversed(range(len(li))) or range(len(li))[::-1] (they’re roughly equivalent too, unlike most comparisons between reversed and the reversing slice; modern Py3 range can be sliced in reverse to make another lazy range that runs backwards, so it’s equally performant). Looks like wim went with the former.

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.

a significant downside is the fact you will always iterate through the whole list. you should emphasis that in your answer.

Use default=None to avoid error «ValueError: max() arg is an empty sequence» max((loc for loc, val in enumerate(li) if val == ‘a’), default=None)

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

Источник

Find index of last occurrence of a substring in a string

I want to find the position (or index) of the last occurrence of a certain substring in given input string str . For example, suppose the input string is str = ‘hello’ and the substring is target = ‘l’ , then it should output 3. How can I do this?

12 Answers 12

Also don’t use str as variable name or you’ll shadow the built-in str() .

>>> s = 'Hello StackOverflow Hi everybody' >>> print( s.rfind('H') ) 20 >>> print( s.rindex('H') ) 20 >>> print( s.rfind('other') ) -1 >>> print( s.rindex('other') ) Traceback (most recent call last): File "", line 1, in ValueError: substring not found 

The difference is when the substring is not found, rfind() returns -1 while rindex() raises an exception ValueError (Python2 link: ValueError ).

If you do not want to check the rfind() return code -1 , you may prefer rindex() that will provide an understandable error message. Else you may search for minutes where the unexpected value -1 is coming from within your code.

Example: Search of last newline character

>>> txt = '''first line . second line . third line''' >>> txt.rfind('\n') 22 >>> txt.rindex('\n') 22 

Hi @JacobSchneider. I have added an example according to your comment. I do not understand your issue. Please provide more information: Python version, Content of your string. Cheers

>>> 'hello'.rindex('l') 3 >>> 'hello'.index('l') 2 

Not trying to resurrect an inactive post, but since this hasn’t been posted yet.

(This is how I did it before finding this question)

s = "hello" target = "l" last_pos = len(s) - 1 - s[::-1].index(target) 

Explanation: When you’re searching for the last occurrence, really you’re searching for the first occurrence in the reversed string. Knowing this, I did s[::-1] (which returns a reversed string), and then indexed the target from there. Then I did len(s) — 1 — the index found because we want the index in the unreversed (i.e. original) string.

Watch out, though! If target is more than one character, you probably won’t find it in the reversed string. To fix this, use last_pos = len(s) — 1 — s[::-1].index(target[::-1]) , which searches for a reversed version of target .

Источник

Оцените статью