So finden Sie das letzte Vorkommen eines Elements in einer Python-Liste

76

Angenommen, ich habe diese Liste:

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

Soweit mir die Hilfe gezeigt hat, gibt es keine eingebaute Funktion, die das letzte Vorkommen eines Strings zurückgibt (wie die Umkehrung von index). Wie kann ich also im Grunde das letzte Vorkommen "a"in der angegebenen Liste finden?

Shaokan
quelle

Antworten:

90

Wenn Sie tatsächlich nur einzelne Buchstaben verwenden, wie in Ihrem Beispiel gezeigt, str.rindexwürde dies problemlos funktionieren. Dies löst eine ValueErrorFehlerklasse aus, die vorhanden ist, wenn es kein solches Element gibt list.index. Demo:

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

Für den allgemeineren Fall könnten Sie list.indexauf der umgekehrten Liste verwenden:

>>> len(li) - 1 - li[::-1].index('a')
6

Durch das Schneiden hier wird eine Kopie der gesamten Liste erstellt. Das ist in Ordnung für kurze Listen, aber für den Fall, dass lies sehr groß ist, kann die Effizienz mit einem faulen Ansatz besser sein:

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

Einzeiler-Version:

next(i for i in reversed(range(len(li))) if li[i] == 'a')
wim
quelle
2
YMMV, aber für dieses Beispiel len(li) - next(i for i, v in enumerate(reversed(li), 1) if v == 'a')ist ein wenig schneller für mich
John La Rooy
11
Gibt es str.rindex()einen Grund, warum es keinen gibt list.rindex()?
Chris_Rands
2
@Chris_Rands: Besser als range(len(li)-1, -1, -1)entweder reversed(range(len(li)))oder range(len(li))[::-1](sie sind im Gegensatz zu den meisten Vergleichen zwischen reversedund dem Umkehr-Slice auch ungefähr gleichwertig ; modernes Py3 rangekann umgekehrt geschnitten werden, um ein anderes Faulpelz zu machen range, das rückwärts läuft, also ist es gleichermaßen performant). Sieht so aus, als wäre ich mit dem ersteren gegangen.
ShadowRanger
@ShadowRanger Ja, wim hat seine Antwort bearbeitet, seit ich diesen Kommentar geschrieben habe. Dies war die Antwort zum Zeitpunkt stackoverflow.com/revisions/… Ich werde meinen früheren Kommentar (und diesen) löschen, um Verwirrung zu vermeiden
Chris_Rands
Pythonic-Versionnext(len(a)-i-1 for i, x in enumerate(reversed(a)) if x == 3)
coder.in.me
36

Ein Einzeiler, der Ignacios ähnelt, nur etwas einfacher / klarer wäre

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

Es scheint mir sehr klar und pythonisch: Sie suchen nach dem höchsten Index, der einen übereinstimmenden Wert enthält. Keine Nexts, Lambdas, Reverseds oder Itertools erforderlich.

Alcalde
quelle
7
Wie @Isaac hervorhebt, iteriert dies immer über alle N Elemente von li.
smci
1
Dies ist perfekt. Vielleicht nicht ideal für einen großen Datensatz, aber für kleine Datenmengen ist es großartig.
Srock
Ein wesentlicher Nachteil ist die Tatsache, dass Sie immer die gesamte Liste durchlaufen. Sie sollten dies in Ihrer Antwort hervorheben.
Guyarad
17

Viele der anderen Lösungen erfordern das Durchlaufen der gesamten Liste. Das tut nicht.

def find_last(lst, elm):
  gen = (len(lst) - 1 - i for i, v in enumerate(reversed(lst)) if v == elm)
  return next(gen, None)

Bearbeiten: Im Nachhinein scheint dies unnötige Zauberei zu sein. Ich würde stattdessen so etwas tun:

def find_last(lst, sought_elt):
    for r_idx, elt in enumerate(reversed(lst)):
        if elt == sought_elt:
            return len(lst) - 1 - r_idx
Isaac
quelle
Ich vermute, dass der Unterschied eine C-Schleife gegenüber einer Python-Schleife ist.
Amit Naidu
8
>>> (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
Ignacio Vazquez-Abrams
quelle
2
Warum nicht reversed(enumerate(li))?
Isaac
31
Weil es mir vor 3 Jahren nicht eingefallen ist.
Ignacio Vazquez-Abrams
Seltsamerweise reversed(enumerate(li))führt dies zu einem Fehler, der lautet argument to reversed() must be a sequence! Und das steht auch für reversed((y for y in enumarete(li))!
trss
2
Sinnvoll, dass reversed()Iteratoren im Allgemeinen, einschließlich Generatoren, nicht ausgeführt werden können. Das enumerate(reversed(li))Anpassen der Indexkomponente der aufgezählten Tupel ist eine Problemumgehung, bei der keine Kopie der Liste erstellt wird.
trss
7

Ich mag sowohl Wims als auch Ignacios Antworten. Ich denke jedoch, dass es itertoolstrotz Lambda eine etwas besser lesbare Alternative gibt. (Für Python 3; für Python 2 xrangeanstelle von range).

>>> from itertools import dropwhile
>>> l = list('apples')
>>> l.index('p')
1
>>> next(dropwhile(lambda x: l[x] != 'p', reversed(range(len(l)))))
2

Dies löst eine StopIterationAusnahme aus, wenn das Element nicht gefunden wird. Sie könnten das fangen und ValueErrorstattdessen ein erhöhen , damit sich das genauso verhältindex .

Als Funktion definiert, um die lambdaVerknüpfung zu vermeiden :

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")

Es funktioniert auch für Nicht-Zeichen. Geprüft:

>>> rindex(['apples', 'oranges', 'bananas', 'apples'], 'apples')
3
senderle
quelle
Python 3 erlaubt jetzt ein Standardargument next, so dass Sie dietry... except
PM 2Ring
@ PM2Ring, ich weiß nicht, warum Sie hier ein Standardargument verwenden möchten. Dann müssten Sie eine ifErklärung haben und eine ValueErrordarin erheben . (Denken Sie daran, dass wir versuchen, die indexAPI genau zu duplizieren , was bedeutet, dass ein ausgelöst wird, ValueErrorwenn das Element nicht gefunden werden kann.) Die Verwendung tryin diesem Kontext ist idiomatischer und, wie ich vermute, effizienter.
senderle
Fairer Ruf. Vielleicht IndexErroreher erhöhen als ValueError, um im Einklang zu sein .index?
PM 2Ring
IndexErrordient zum Übergeben eines falschen Index. Wenn Sie einen fehlenden Wert übergeben, indexwird a generiert ValueError.
senderle
5

Mit dict

Sie können die Tatsache verwenden, dass Wörterbuchschlüssel eindeutig sind und beim Erstellen eines Schlüssels mit Tupeln nur die letzte Zuweisung eines Werts für einen bestimmten Schlüssel verwendet wird. Wie in anderen Antworten angegeben, ist dies für kleine Listen in Ordnung, erstellt jedoch ein Wörterbuch für alle eindeutigen Werte und ist für große Listen möglicherweise nicht effizient.

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

6
piRSquared
quelle
2

Ich kam hierher in der Hoffnung, jemanden zu finden, der bereits die effizienteste Version von geschrieben hatte list.rindex, die die vollständige Oberfläche von list.index(einschließlich optional startund stopParameter) bereitstellte . Das habe ich in den Antworten auf diese Frage nicht gefunden, oder hier oder hier oder hier . Also habe ich das selbst zusammengestellt ... indem ich Vorschläge aus anderen Antworten auf diese und die anderen Fragen verwendet habe.

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)

Die len(seq) - 1 - next(i for i,v in enumerate(reversed(seq)) if v == value)in mehreren anderen Antworten vorgeschlagene Technik kann platzsparender sein: Es muss keine umgekehrte Kopie der vollständigen Liste erstellt werden. Aber in meinen (beiläufigen, gelegentlichen) Tests ist es ungefähr 50% langsamer.

zweifelhaft
quelle
0

Verwenden Sie eine einfache Schleife:

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))
Laurent LAPORTE
quelle
0
last_occurence=len(yourlist)-yourlist[::-1].index(element)-1

So einfach ist das. Sie müssen keine Funktion importieren oder erstellen.

Prabu M.
quelle
0
lastIndexOf = lambda array, item: len(array) - (array[::-1].index(item)) - 1
Sapphire_Brick
quelle
-1
def rindex(lst, val):
    try:
        return next(len(lst)-i for i, e in enumerate(reversed(lst), start=1) if e == val)
    except StopIteration:
        raise ValueError('{} is not in list'.format(val))
user2426679
quelle
-1

val = [1,2,2,2,2,2,4,5].

Wenn Sie das letzte Vorkommen von 2 finden müssen

last_occurence = (len(val) -1) - list(reversed(val)).index(2)

user2782561
quelle
-1

Hier ist ein kleiner Einzeiler, um den letzten Index zu erhalten, zu verwenden enumerateund ein Listenverständnis zu erhalten:

li = ["a", "b", "a", "c", "x", "d", "a", "6"]
[l[0] for l in enumerate(li) if l[1] == "a"][-1]
Quazgar
quelle