Hoe een lijst met tuples zoeken in Python

Dus ik heb een lijst met tupels zoals deze:

[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

Ik wil deze lijst voor een tuple waarvan de getalswaarde gelijk is aan iets.

Zodat als ik search(53)doe, het de indexwaarde van 2

teruggeeft

Is er een gemakkelijke manier om dit te doen?


Antwoord 1, autoriteit 100%

[i for i, v in enumerate(L) if v[0] == 53]

Antwoord 2, autoriteit 53%

tl;dr

Een generator-expressieis waarschijnlijk de meest performante en eenvoudige oplossing voor jouw probleem:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
result = next((i for i, v in enumerate(l) if v[0] == 53), None)
# 2

Uitleg

Er zijn verschillende antwoorden die een eenvoudige oplossing voor deze vraag bieden met lijstbegrippen.
Hoewel deze antwoorden volkomen correct zijn, zijn ze niet optimaal.
Afhankelijk van uw gebruikssituatie kunnen er aanzienlijke voordelen zijn als u een paar eenvoudige wijzigingen aanbrengt.

Het grootste probleem dat ik zie bij het gebruik van een lijstbegrip voor dit gebruik, is dat de hele lijstwordt verwerkt, hoewel je slechts 1 elementwilt vinden.

Python biedt een eenvoudige constructie die hier ideaal is. Het wordt de generator-expressiegenoemd. Hier is een voorbeeld:

# Our input list, same as before
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
# Call next on our generator expression.
next((i for i, v in enumerate(l) if v[0] == 53), None)

We kunnen verwachten dat deze methode in principe hetzelfde presteert als lijstbegrippen in ons triviale voorbeeld, maar wat als we met een grotere dataset werken?
Dat is waar het voordeel van het gebruik van de generatormethode in het spel komt.
In plaats van een nieuwe lijst te maken, gebruiken we uw bestaande lijst als onze iterable en gebruiken we next()om het eerste item uit onze generator te halen.

Laten we eens kijken hoe deze methoden anders presteren op sommige grotere datasets.
Dit zijn grote lijsten, gemaakt van 10000000 + 1 elementen, met ons doel aan het begin (beste) of einde (slechtste).
We kunnen verifiëren dat beide lijsten even goed presteren met behulp van het volgende lijstbegrip:

Lijst begrippen

“In het ergste geval”

worst_case = ([(False, 'F')] * 10000000) + [(True, 'T')]
print [i for i, v in enumerate(worst_case) if v[0] is True]
# [10000000]
#          2 function calls in 3.885 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.885    3.885    3.885    3.885 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

“Beste geval”

best_case = [(True, 'T')] + ([(False, 'F')] * 10000000)
print [i for i, v in enumerate(best_case) if v[0] is True]
# [0]
#          2 function calls in 3.864 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.864    3.864    3.864    3.864 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

Generatoruitdrukkingen

Dit is mijn hypothese voor generatoren: we zullen zien dat generatoren in het beste geval aanzienlijk beter zullen presteren, maar in het slechtste geval ook.
Deze prestatiewinst is voornamelijk te wijten aan het feit dat de generator lui wordt geëvalueerd, wat betekent dat hij alleen berekent wat nodig is om een ​​waarde op te leveren.

In het ergste geval

# 10000000
#          5 function calls in 1.733 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         2    1.455    0.727    1.455    0.727 so_lc.py:10(<genexpr>)
#         1    0.278    0.278    1.733    1.733 so_lc.py:9(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    1.455    1.455 {next}

Beste geval

best_case  = [(True, 'T')] + ([(False, 'F')] * 10000000)
print next((i for i, v in enumerate(best_case) if v[0] == True), None)
# 0
#          5 function calls in 0.316 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    0.316    0.316    0.316    0.316 so_lc.py:6(<module>)
#         2    0.000    0.000    0.000    0.000 so_lc.py:7(<genexpr>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    0.000    0.000 {next}

WAT?! Het beste geval vernietigthet begrip van de lijst, maar ik had niet verwacht dat het slechtste geval in zo’n mate beter zou presteren dan het begrip van de lijst.
Hoe is dat? Eerlijk gezegd kon ik alleen maar speculeren zonder verder onderzoek.

Neem dit alles met een korreltje zout, ik heb hier geen robuuste profilering uitgevoerd, alleen een paar zeer basale tests. Dit zou voldoende moeten zijn om te beseffen dat een generator-expressie beter presteert voor dit soort lijstzoekopdrachten.

Merk op dat dit allemaal een eenvoudige, ingebouwde python is. We hoeven niets te importeren of bibliotheken te gebruiken.

Ik zag deze techniek voor het zoeken voor het eerst in de Udacity cs212cursus met Peter Norvig.


Antwoord 3, autoriteit 47%

U kunt een lijstbegripgebruiken:

>>> a = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
>>> [x[0] for x in a]
[1, 22, 53, 44]
>>> [x[0] for x in a].index(53)
2

Antwoord 4, autoriteit 30%

Uw tuples zijn in feite sleutel-waardeparen–een python dict–dus:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
val = dict(l)[53]

Bewerken — aha, je zegt dat je de indexwaarde van (53, “xuxa”) wilt. Als dit echtis wat je wilt, moet je de originele lijst herhalen, of misschien een ingewikkelder woordenboek maken:

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]

Antwoord 5, autoriteit 12%

Hmm… nou, de simpele manier die in je opkomt is om het om te zetten in een dictaat

d = dict(thelist)

en toegang tot d[53].

EDIT: Oeps, de eerste keer verkeerd gelezen. Het klinkt alsof je eigenlijk de index wilt krijgen waar een bepaald nummer is opgeslagen. Probeer in dat geval

dict((t[0], i) for i, t in enumerate(thelist))

in plaats van een gewone, oude dict-conversie. Dan zou d[53]2 zijn.


Antwoord 6, autoriteit 7%

Stel dat de lijst lang is en de getallen zich herhalen, overweeg dan om het type SortedListuit de Python Sortedcontainers-module. Het type SortedList houdt de tuples automatisch op nummer en maakt snel zoeken mogelijk.

Bijvoorbeeld:

from sortedcontainers import SortedList
sl = SortedList([(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")])
# Get the index of 53:
index = sl.bisect((53,))
# With the index, get the tuple:
tup = sl[index]

Dit werkt een stuk sneller dan de suggestie voor het begrijpen van de lijst door een binaire zoekopdracht uit te voeren. De woordenboeksuggestie zal nog sneller zijn, maar zal niet werken als er dubbele getallen met verschillende tekenreeksen kunnen zijn.

Als er dubbele nummers zijn met verschillende tekenreeksen, moet u nog een stap nemen:

end = sl.bisect((53 + 1,))
results = sl[index:end]

Door 54 te halveren, vinden we de eindindex voor ons segment. Dit zal aanzienlijk sneller zijn op lange lijsten in vergelijking met het geaccepteerde antwoord.


Antwoord 7, autoriteit 2%

Op een andere manier.

zip(*a)[0].index(53)

Antwoord 8

[k voor k,v in l if v ==’delicia‘]

hier is de lijst met tuples-[(1,”juca”),(22,”james”),(53,”xuxa”),(44,”delicia”)]

En in plaats van het om te zetten in een dictaat, gebruiken we lijstbegrip.

*Key* in Key,Value in list, where value = **delicia**

Other episodes