oggetti - ordinare lista python




Trovare l'indice di un oggetto dato un elenco che lo contiene in Python (18)

Trovare l'indice di un oggetto dato un elenco che lo contiene in Python

Per una lista ["foo", "bar", "baz"] e un elemento nella lista "bar" , qual è il modo più pulito per ottenere il suo indice (1) in Python?

Beh, certo, c'è il metodo index, che restituisce l'indice della prima occorrenza:

>>> l = ["foo", "bar", "baz"]
>>> l.index('bar')
1

Ci sono un paio di problemi con questo metodo:

  • se il valore non è nella lista, otterrai un ValueError
  • se più di uno dei valori è presente nell'elenco, ottieni solo l'indice per il primo

Nessun valore

Se il valore potrebbe essere mancante, è necessario rilevare l' ValueError .

Puoi farlo con una definizione riutilizzabile come questa:

def index(a_list, value):
    try:
        return a_list.index(value)
    except ValueError:
        return None

E usalo in questo modo:

>>> print(index(l, 'quux'))
None
>>> print(index(l, 'bar'))
1

E il lato negativo di questo è che probabilmente avrai un controllo per se il valore restituito is o is not None:

result = index(a_list, value)
if result is not None:
    do_something(result)

Più di un valore nell'elenco

Se potresti avere più occorrenze, non otterrai informazioni complete con list.index :

>>> l.append('bar')
>>> l
['foo', 'bar', 'baz', 'bar']
>>> l.index('bar')              # nothing at index 3?
1

Puoi enumerare in una lista la comprensione degli indici:

>>> [index for index, v in enumerate(l) if v == 'bar']
[1, 3]
>>> [index for index, v in enumerate(l) if v == 'boink']
[]

Se non si verificano occorrenze, è possibile verificarlo con il controllo booleano del risultato oppure non fare nulla se si esegue il loop dei risultati:

indexes = [index for index, v in enumerate(l) if v == 'boink']
for index in indexes:
    do_something(index)

Migliori dati munging con i panda

Se hai dei panda, puoi facilmente ottenere queste informazioni con un oggetto Series:

>>> import pandas as pd
>>> series = pd.Series(l)
>>> series
0    foo
1    bar
2    baz
3    bar
dtype: object

Un controllo di confronto restituirà una serie di booleani:

>>> series == 'bar'
0    False
1     True
2    False
3     True
dtype: bool

Passa quella serie di booleani alla serie tramite notazione degli iscritti e ottieni solo i membri corrispondenti:

>>> series[series == 'bar']
1    bar
3    bar
dtype: object

Se si desidera solo gli indici, l'attributo index restituisce una serie di numeri interi:

>>> series[series == 'bar'].index
Int64Index([1, 3], dtype='int64')

E se li vuoi in una lista o in una tupla, passali al costruttore:

>>> list(series[series == 'bar'].index)
[1, 3]

Sì, potresti usare anche una lista di enumerazione, ma non è altrettanto elegante, secondo me - stai facendo test per l'uguaglianza in Python, invece di lasciare che il codice incorporato scritto in C lo gestisca:

>>> [i for i, value in enumerate(l) if value == 'bar']
[1, 3]

È un problema XY ?

Il problema XY è chiedere la tua soluzione tentata piuttosto che il tuo problema reale.

Perché pensi di aver bisogno dell'indice dato un elemento in una lista?

Se conosci già il valore, perché ti importa dove si trova in una lista?

Se il valore non è lì, catturare l' ValueError è piuttosto prolisso - e preferisco evitarlo.

Di solito sto ripetendo l'elenco, quindi di solito manterrò un puntatore a qualsiasi informazione interessante, ottenendo l' indice con enumerazione.

Se stai mungendo i dati, probabilmente dovresti usare i panda - che ha strumenti molto più eleganti dei puri soluzioni alternative di Python che ho mostrato.

Non ricordo di aver bisogno di list.index , me stesso. Tuttavia, ho guardato attraverso la libreria standard di Python, e vedo alcuni usi eccellenti per questo.

Ci sono molti, molti usi per esso in idlelib , per la GUI e l'analisi del testo.

Il modulo di keyword lo usa per trovare marcatori di commenti nel modulo per rigenerare automaticamente l'elenco di parole chiave in esso tramite metaprogrammazione.

In Lib / mailbox.py sembra utilizzarlo come una mappatura ordinata:

key_list[key_list.index(old)] = new

e

del key_list[key_list.index(key)]

In Lib / http / cookiejar.py, sembra essere usato per ottenere il mese successivo:

mon = MONTHS_LOWER.index(mon.lower())+1

In Lib / tarfile.py simile alle distutils per ottenere una fetta fino a un oggetto:

members = members[:members.index(tarinfo)]

In Lib / pickletools.py:

numtopop = before.index(markobject)

Ciò che questi usi sembrano avere in comune è che sembrano operare su elenchi di dimensioni vincolate (importanti a causa del tempo di ricerca O (n) per list.index ), e sono principalmente utilizzati nell'analisi (e nell'interfaccia utente nel caso di Inattivo).

Mentre ci sono casi d'uso per questo, sono abbastanza rari. Se ti trovi alla ricerca di questa risposta, chiediti se quello che stai facendo è l'uso più diretto degli strumenti forniti dalla lingua per il tuo caso d'uso.

Per una lista ["foo", "bar", "baz"] e un elemento nella lista "bar" , come faccio a ottenere il suo indice (1) in Python?


E ora qualcosa di completamente diverso...

... come confermare l'esistenza dell'oggetto prima di ottenere l'indice. La cosa bella di questo approccio è che la funzione restituisce sempre un elenco di indici, anche se è una lista vuota. Funziona anche con le stringhe.

def indices(l, val):
    """Always returns a list containing the indices of val in the_list"""
    retval = []
    last = 0
    while val in l[last:]:
            i = l[last:].index(val)
            retval.append(last + i)
            last += i + 1   
    return retval

l = ['bar','foo','bar','baz','bar','bar']
q = 'bar'
print indices(l,q)
print indices(l,'bat')
print indices('abcdaababb','a')

Quando incollato in una finestra python interattiva:

Python 2.7.6 (v2.7.6:3a1db0d2747e, Nov 10 2013, 00:42:54) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def indices(the_list, val):
...     """Always returns a list containing the indices of val in the_list"""
...     retval = []
...     last = 0
...     while val in the_list[last:]:
...             i = the_list[last:].index(val)
...             retval.append(last + i)
...             last += i + 1   
...     return retval
... 
>>> l = ['bar','foo','bar','baz','bar','bar']
>>> q = 'bar'
>>> print indices(l,q)
[0, 2, 4, 5]
>>> print indices(l,'bat')
[]
>>> print indices('abcdaababb','a')
[0, 4, 5, 7]
>>> 

Aggiornare

Dopo un altro anno di sviluppo di python a testa in giù, sono un po 'imbarazzato dalla mia risposta originale, quindi per impostare le cose in chiaro, si può certamente usare il codice di cui sopra; tuttavia, il modo molto più idiomatico per ottenere lo stesso comportamento sarebbe quello di utilizzare la comprensione delle liste, insieme alla funzione enumerate ().

Qualcosa come questo:

def indices(l, val):
    """Always returns a list containing the indices of val in the_list"""
    return [index for index, value in enumerate(l) if value == val]

l = ['bar','foo','bar','baz','bar','bar']
q = 'bar'
print indices(l,q)
print indices(l,'bat')
print indices('abcdaababb','a')

Il che, incollato in una finestra python interattiva, produce:

Python 2.7.14 |Anaconda, Inc.| (default, Dec  7 2017, 11:07:58) 
[GCC 4.2.1 Compatible Clang 4.0.1 (tags/RELEASE_401/final)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def indices(l, val):
...     """Always returns a list containing the indices of val in the_list"""
...     return [index for index, value in enumerate(l) if value == val]
... 
>>> l = ['bar','foo','bar','baz','bar','bar']
>>> q = 'bar'
>>> print indices(l,q)
[0, 2, 4, 5]
>>> print indices(l,'bat')
[]
>>> print indices('abcdaababb','a')
[0, 4, 5, 7]
>>> 

E ora, dopo aver esaminato questa domanda e tutte le risposte, mi rendo conto che questo è esattamente ciò che suggerito nella sua precedente risposta . All'epoca in cui originariamente ho risposto a questa domanda, non ho nemmeno visto quella risposta, perché non l'ho capita. Spero che il mio esempio un po 'più verboso aiuti la comprensione.

Se la singola riga di codice sopra non ha ancora senso per te, ti raccomando caldamente la "comprensione di python list di Google" e prenditi qualche minuto per familiarizzare. È solo una delle molte potenti funzionalità che rendono piacevole l'utilizzo di Python per lo sviluppo del codice.


Ottenere tutte le occorrenze e la posizione di uno o più oggetti (identici) in un elenco

Con enumerate (alist) è possibile memorizzare il primo elemento (n) che è l'indice della lista quando l'elemento x è uguale a quello che si cerca.

>>> alist = ['foo', 'spam', 'egg', 'foo']
>>> foo_indexes = [n for n,x in enumerate(alist) if x=='foo']
>>> foo_indexes
[0, 3]
>>>

Facciamo la nostra funzione findindex

Questa funzione prende l'articolo e l'elenco come argomenti e restituisce la posizione dell'elemento nell'elenco, come abbiamo visto prima.

def indexlist(item2find, list_or_string):
  "Returns all indexes of an item in a list or a string"
  return [n for n,item in enumerate(list_or_string) if item==item2find]

print(indexlist("1", "010101010"))

Produzione

[1, 3, 5, 7]

Semplice

for n, i in enumerate([1, 2, 3, 4, 1]):
    if i == 1:
        print(n)

Produzione:

0
4

C'è una risposta più funzionale a questo.

list(filter(lambda x: x[1]=="bar",enumerate(["foo", "bar", "baz", "bar", "baz", "bar", "a", "b", "c"])))

Forma più generica:

def get_index_of(lst, element):
    return list(map(lambda x: x[0],\
       (list(filter(lambda x: x[1]==element, enumerate(lst))))))

Come indicato da @TerryA, molte risposte discutono su come trovare un indice.

more_itertools è una libreria di terze parti con strumenti per individuare più indici all'interno di un iterabile.

Dato

import more_itertools as mit


iterable = ["foo", "bar", "baz", "ham", "foo", "bar", "baz"]

Codice

Trova indici di più osservazioni:

list(mit.locate(iterable, lambda x: x == "bar"))
# [1, 5]

Prova più oggetti:

list(mit.locate(iterable, lambda x: x in {"bar", "ham"}))
# [1, 3, 5]

Vedi anche altre opzioni con more_itertools.locate . Installa tramite more_itertools .


Devi impostare una condizione per verificare se l'elemento che stai cercando è nell'elenco

if 'your_element' in mylist:
    print mylist.index('your_element')
else:
    print None

La maggior parte delle risposte spiega come trovare un singolo indice , ma i loro metodi non restituiscono più indici se l'elemento è presente nell'elenco più volte. Usa enumerate() :

for i, j in enumerate(['foo', 'bar', 'baz']):
    if j == 'bar':
        print(i)

La funzione index() restituisce solo la prima occorrenza, mentre enumerate() restituisce tutte le occorrenze.

Come una lista di comprensione:

[i for i, j in enumerate(['foo', 'bar', 'baz']) if j == 'bar']

Ecco anche un'altra piccola soluzione con itertools.count() (che è praticamente lo stesso approccio enumerato):

from itertools import izip as zip, count # izip for maximum efficiency
[i for i, j in zip(count(), ['foo', 'bar', 'baz']) if j == 'bar']

Questo è più efficiente per gli elenchi più grandi rispetto all'utilizzo di enumerate() :

$ python -m timeit -s "from itertools import izip as zip, count" "[i for i, j in zip(count(), ['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 174 usec per loop
$ python -m timeit "[i for i, j in enumerate(['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 196 usec per loop

Per ottenere tutti gli indici:

 indexes = [i for i,x in enumerate(xs) if x == 'foo']

Poiché gli elenchi Python sono a base zero, possiamo utilizzare la funzione integrata di zip come segue:

>>> [i for i,j in zip(range(len(haystack)), haystack) if j == 'needle' ]

dove "pagliaio" è l'elenco in questione e "ago" è l'elemento da cercare.

(Nota: qui stiamo iterando usando i per ottenere gli indici, ma se abbiamo bisogno piuttosto di concentrarci sugli elementi possiamo passare a j.)


Questa soluzione non è potente come altri, ma se sei un principiante e conosci solo i loop è ancora possibile trovare il primo indice di un oggetto evitando l'errore ValueError:

def find_element(p,t):
    i = 0
    for e in p:
        if e == t:
            return i
        else:
            i +=1
    return -1

Semplicemente puoi andare con

a = [['hand', 'head'], ['phone', 'wallet'], ['lost', 'stock']]
b = ['phone', 'lost']

res = [[x[0] for x in a].index(y) for y in b]

Trovare l'indice dell'articolo x nell'elenco L :

idx = L.index(x) if (x in L) else -1

Tutti gli indici con la funzione zip :

get_indexes = lambda x, xs: [i for (y, i) in zip(xs, range(len(xs))) if x == y]

print get_indexes(2, [1, 2, 3, 4, 5, 6, 3, 2, 3, 2])
print get_indexes('f', 'xsfhhttytffsafweef')

Un problema si presenterà se l'elemento non è nella lista. Questa funzione gestisce il problema:

# if element is found it returns index of element else returns None

def find_element_in_list(element, list_element):
    try:
        index_element = list_element.index(element)
        return index_element
    except ValueError:
        return None

Una cosa che è molto utile nell'apprendimento di Python è l'uso della funzione di guida interattiva:

>>> help(["foo", "bar", "baz"])
Help on list object:

class list(object)
 ...

 |
 |  index(...)
 |      L.index(value, [start, [stop]]) -> integer -- return first index of value
 |

che ti condurrà spesso al metodo che stai cercando.


Una variante della risposta di FMc e user7177 fornirà un comando che può restituire tutti gli indici per qualsiasi voce:

>>> a = ['foo','bar','baz','bar','any', 'foo', 'much']
>>> l = dict(zip(set(a), map(lambda y: [i for i,z in enumerate(a) if z is y ], set(a))))
>>> l['foo']
[0, 5]
>>> l ['much']
[6]
>>> l
{'baz': [2], 'foo': [0, 5], 'bar': [1, 3], 'any': [4], 'much': [6]}
>>> 

Puoi anche usarlo come un unico liner per ottenere tutti gli indici per una singola voce. Non ci sono garanzie di efficienza, anche se ho usato il set (a) per ridurre il numero di volte in cui viene chiamata la lambda.


>>> ["foo", "bar", "baz"].index("bar")
1

Riferimento: Strutture dati> Altro su elenchi

I caveat seguono

Si noti che mentre questo è forse il modo più pulito per rispondere alla domanda, l' index è un componente piuttosto debole dell'API list e non riesco a ricordare l'ultima volta che l'ho usato in rabbia. Mi è stato fatto notare nei commenti che, poiché questa risposta è pesantemente referenziata, dovrebbe essere resa più completa. list.index alcuni avvertimenti su list.index . Vale probabilmente la pena dare inizialmente un'occhiata alla docstring per questo:

>>> print(list.index.__doc__)
L.index(value, [start, [stop]]) -> integer -- return first index of value.
Raises ValueError if the value is not present.

Complessità di tempo lineare nella lunghezza dell'elenco

Una chiamata di index controlla ogni elemento della lista in ordine, finché non trova una corrispondenza. Se la tua lista è lunga, e non sai approssimativamente dove si trova nella lista, questa ricerca potrebbe diventare un collo di bottiglia. In tal caso, dovresti prendere in considerazione una diversa struttura dei dati. Nota che se sai approssimativamente dove trovare la partita, puoi dare un indizio index . Ad esempio, in questo frammento, l.index(999_999, 999_990, 1_000_000) è all'incirca cinque ordini di grandezza più veloce di straight l.index(999_999) , perché il primo deve cercare solo 10 voci, mentre il secondo cerca un milione:

>>> import timeit
>>> timeit.timeit('l.index(999_999)', setup='l = list(range(0, 1_000_000))', number=1000)
9.356267921015387
>>> timeit.timeit('l.index(999_999, 999_990, 1_000_000)', setup='l = list(range(0, 1_000_000))', number=1000)
0.0004404920036904514

Restituisce solo l'indice della prima corrispondenza al suo argomento

Una chiamata per index ricerche nell'elenco finché non trova una corrispondenza e si ferma lì. Se ti aspetti di aver bisogno di indici di più corrispondenze, dovresti usare una comprensione di lista o un'espressione di generatore.

>>> [1, 1].index(1)
0
>>> [i for i, e in enumerate([1, 2, 1]) if e == 1]
[0, 2]
>>> g = (i for i, e in enumerate([1, 2, 1]) if e == 1)
>>> next(g)
0
>>> next(g)
2

La maggior parte dei luoghi in cui una volta avrei utilizzato l' index , ora utilizzo una comprensione di lista o un'espressione di generatore perché sono più generalizzabili. Quindi, se stai pensando di raggiungere l' index , dai un'occhiata a queste eccellenti funzionalità di Python.

Genera se elemento non presente nella lista

Una chiamata per index produce un ValueError se l'elemento non è presente.

>>> [1, 1].index(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 2 is not in list

Se l'articolo potrebbe non essere presente nella lista, dovresti

  1. Controllalo prima con item in my_list (approccio pulito, leggibile) o
  2. Avvolgi la chiamata index in un blocco try/except che ValueError (probabilmente più veloce, almeno quando l'elenco da cercare è lungo e l'elemento è solitamente presente).

a = ["foo","bar","baz",'bar','any','much']

indexes = [index for index in range(len(a)) if a[index] == 'bar']




list