web-dev-qa-db-fra.com

Comment trouver la dernière occurrence d'un élément dans une liste Python

Dis que j'ai cette liste:

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

Pour ce qui est de l'aide, il n'y a pas de fonction intégrée qui retourne la dernière occurrence d'une chaîne (comme l'inverse de index). Donc, en gros, comment trouver la dernière occurrence de "a" dans la liste donnée?

55
Shaokan

Si vous n'utilisez que des lettres simples, comme indiqué dans votre exemple, alors str.rindex fonctionnerait facilement. Cela déclenche une ValueError s'il n'y a pas d'élément de ce type, la même classe d'erreur que list.index déclencherait. Démo:

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

Pour le cas plus général, vous pouvez utiliser list.index dans la liste inversée: 

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

Le découpage ici crée un copy de la liste entière. C'est bien pour les listes courtes, mais dans le cas où li est très grand, l'efficacité peut être meilleure avec une approche paresseuse:

def list_rindex(li, x):
    for i in reversed(range(len(li))):
        if li[i] == x:
            return x
    raise ValueError("{} is not in list".format(x))

Version monocouche:

next(i for i in reversed(range(len(li))) if li[i] == 'a')
68
wim

Un one-line qui ressemble à Ignacio, sauf qu’un peu plus simple/plus clair serait

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

Cela me semble très clair et Pythonic: vous recherchez l'index le plus élevé contenant une valeur correspondante. Pas de liens, lambdas, inversés ou outils nécessaires. 

26
alcalde

Beaucoup des autres solutions nécessitent une itération sur toute la liste. Cela ne veut pas.

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: Avec le recul, cela semble être une magie inutile. Je ferais quelque chose comme ça à la place:

def find_last(lst, sought_elt):
    for r_idx, elt in enumerate(reversed(lst)):
        if elt == sought_elt:
            return len(lst) - 1 - r_idx
14
Isaac

J'aime les réponses de wim et de Ignacio . Cependant, je pense que itertools fournit une alternative légèrement plus lisible, nonobstant lambda. (Pour Python 3; pour Python 2, utilisez xrange au lieu de range).

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

Cela déclenchera une exception StopIteration si l'élément n'est pas trouvé; vous pouvez attraper cela et lever une ValueError à la place, pour que cela se comporte comme index .

Défini en tant que fonction, en évitant le raccourci 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")

Cela fonctionne aussi pour les non-personnages. Testé:

>>> rindex(['apples', 'oranges', 'bananas', 'apples'], 'apples')
3
6
senderle
>>> (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
6

Vous pouvez utiliser le fait que les clés de dictionnaire sont uniques et que, lors de la construction avec des nuplets, seule la dernière affectation d’une valeur pour une clé particulière sera utilisée. Comme indiqué dans d'autres réponses, cela convient aux petites listes, mais il crée un dictionnaire pour toutes les valeurs uniques et peut ne pas être efficace pour les grandes listes.

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

6
3
piRSquared

Je suis venu ici dans l'espoir de trouver quelqu'un qui avait déjà écrit la version la plus efficace de list.rindex, qui fournit l'interface complète de list.index (y compris les paramètres optionnels start et stop). Je n'ai pas trouvé cela dans les réponses à cette question, ou ici ou ici ou ici . Donc, j'ai moi-même mis cela en place ... en utilisant les suggestions d'autres réponses à cette question et aux autres.

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)

La technique utilisant len(seq) - 1 - next(i for i,v in enumerate(reversed(seq)) if v == value), suggérée dans plusieurs autres réponses, peut être plus efficace en termes d'espace: elle n'a pas besoin de créer une copie inversée de la liste complète. Mais dans mes tests (désinvoltes, occasionnels), il est environ 50% plus lent.

2
dubiousjim

Utilisez une simple boucle:

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

tout aussi facile que cela. Pas besoin d'importer ou de créer une fonction.

1
Prabu M

Voici un petit guide pour obtenir le dernier index en utilisant enumerate et une liste de compréhension:

li = ["a", "b", "a", "c", "x", "d", "a", "6"]
[l[0] for l in enumerate(li) if l[1] == "a"][-1]
0
quazgar
from array import array
fooa = array('i', [1,2,3])
fooa.reverse()  # [3,2,1]
fooa.index(1)
>>> 2
0
cop4587
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))
0
user2426679