Wie sortiere ich eine Liste von Wörterbüchern nach einem Wert des Wörterbuchs?

1897

Ich habe eine Liste von Wörterbüchern und möchte, dass jedes Element nach bestimmten Eigenschaftswerten sortiert wird.

Berücksichtigen Sie das folgende Array:

[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

Wenn sortiert nach name, sollte werden

[{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]
masi
quelle
Lesen Sie die Antwort und schauen Sie auf operator.itemgetter . Kann ich im selben Prozess nach mehreren Werten sortieren (zum Beispiel haben wir [{'name':'Bart', 'age':10, 'note':3},{'name':'Homer','age':10,'note':2},{'name':'Vasile','age':20,'note':3}] Und zu verwenden: from operator import itemgetter newlist = sorted(old_list, key=itemgetter(-'note','name') BEARBEITEN: Getestet, und es funktioniert, aber ich weiß nicht, wie man DESC notiert und ASC benennt.
Claudiu

Antworten:

2469

Mit einem Schlüssel anstelle eines cmp sieht es möglicherweise sauberer aus:

newlist = sorted(list_to_be_sorted, key=lambda k: k['name']) 

oder wie JFSebastian und andere vorgeschlagen haben,

from operator import itemgetter
newlist = sorted(list_to_be_sorted, key=itemgetter('name')) 

Der Vollständigkeit halber (wie in den Kommentaren von fitzgeraldsteele ausgeführt) reverse=Truezur absteigenden Sortierung hinzufügen

newlist = sorted(l, key=itemgetter('name'), reverse=True)
Mario F.
quelle
34
Die Verwendung von Schlüsseln ist nicht nur sauberer, sondern auch effizienter.
JFS
5
Der schnellste Weg wäre, eine Anweisung newlist.reverse () hinzuzufügen. Andernfalls können Sie einen Vergleich wie cmp = Lambda x, y: - cmp (x ['Name'], y ['Name']) definieren.
Mario F
3
Wenn der Sortierwert eine Zahl ist, könnte man sagen: Lambda k: (k ['Alter'] * -1), um eine umgekehrte Sortierung zu erhalten
Philluminati
2
Dies gilt auch für eine Liste von Tupeln, wenn Sie verwenden , itemgetter(i)wo ider Index des Tupelelement ist zu sortieren.
Radicand
42
itemgetterakzeptiert mehr als ein Argument: itemgetter(1,2,3)ist eine Funktion, die ein Tupel wie zurückgibt obj[1], obj[2], obj[3], sodass Sie damit komplexe Sortierungen durchführen können.
Bakuriu
166
import operator

So sortieren Sie die Liste der Wörterbücher nach key = 'name':

list_of_dicts.sort(key=operator.itemgetter('name'))

So sortieren Sie die Liste der Wörterbücher nach key = 'age':

list_of_dicts.sort(key=operator.itemgetter('age'))
Cedbeu
quelle
9
Wie auch immer, um Name und Alter zu kombinieren? (wie in SQL ORDER BY Name, Alter?)
Monojohnny
28
@monojohnny: ja, lass einfach den Schlüssel ein Tupel zurückgeben , key=lambda k: (k['name'], k['age']). (oder key=itemgetter('name', 'age')). Tupel cmpvergleichen nacheinander jedes Element. es ist verdammt brillant.
Claudiu
1
In der Dokumentation ( docs.python.org/2/tutorial/datastructures.html ) wird das optionale keyArgument für list.sort()nicht beschrieben. Irgendeine Idee, wo man das findet?
TTT
2
@TTT: Siehe die Bibliotheksdokumentation für listund Freunde.
Kevin
64
my_list = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

my_list.sort(lambda x,y : cmp(x['name'], y['name']))

my_list wird jetzt sein, was Sie wollen.

(3 Jahre später) Bearbeitet, um hinzuzufügen:

Das neue keyArgument ist effizienter und ordentlicher. Eine bessere Antwort sieht jetzt so aus:

my_list = sorted(my_list, key=lambda k: k['name'])

... das Lambda ist, IMO, leichter zu verstehen als operator.itemgetter, aber YMMV.

pjz
quelle
51

Wenn Sie die Liste nach mehreren Schlüsseln sortieren möchten, haben Sie folgende Möglichkeiten:

my_list = [{'name':'Homer', 'age':39}, {'name':'Milhouse', 'age':10}, {'name':'Bart', 'age':10} ]
sortedlist = sorted(my_list , key=lambda elem: "%02d %s" % (elem['age'], elem['name']))

Es ist ziemlich hackisch, da es darauf beruht, die Werte zum Vergleich in eine einzelne Zeichenfolgendarstellung umzuwandeln, aber es funktioniert wie erwartet für Zahlen, einschließlich negativer Zeichen (obwohl Sie Ihre Zeichenfolge mit Null-Auffüllungen entsprechend formatieren müssen, wenn Sie Zahlen verwenden).

Dologan
quelle
2
sortiert mit timsort, das stabil ist, können Sie sortiert mehrmals aufrufen, um eine Sortierung nach mehreren Kriterien zu haben
njzk2
Der Kommentar von njzk2 war mir nicht sofort klar, daher fand ich Folgendes. Sie können einfach zweimal sortieren, wie njzk2 vorschlägt, oder in der oberen Antwort mehrere Argumente an operator.itemgetter übergeben. Link: stackoverflow.com/questions/5212870/…
Permafacture
15
Keine Konvertierung in einen String erforderlich. Geben Sie einfach ein Tupel als Schlüssel zurück.
Winston Ewert
Mehrfaches Sortieren ist die einfachste generische Lösung ohne Hacks: stackoverflow.com/a/29849371/1805397
wouter bolsterlee
30
import operator
a_list_of_dicts.sort(key=operator.itemgetter('name'))

'key' wird verwendet, um nach einem beliebigen Wert zu sortieren, und 'itemgetter' setzt diesen Wert auf das Attribut 'name' jedes Elements.

efotinis
quelle
27
a = [{'name':'Homer', 'age':39}, ...]

# This changes the list a
a.sort(key=lambda k : k['name'])

# This returns a new list (a is not modified)
sorted(a, key=lambda k : k['name']) 
forzagreen
quelle
21

Ich denke du hast gemeint:

[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

Dies würde wie folgt sortiert werden:

sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))
Bartosz Radaczyński
quelle
19

Sie können eine benutzerdefinierte Vergleichsfunktion verwenden oder eine Funktion übergeben, die einen benutzerdefinierten Sortierschlüssel berechnet. Dies ist normalerweise effizienter, da der Schlüssel nur einmal pro Element berechnet wird, während die Vergleichsfunktion mehrmals aufgerufen wird.

Sie könnten es so machen:

def mykey(adict): return adict['name']
x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
sorted(x, key=mykey)

Die Standardbibliothek enthält jedoch eine generische Routine zum Abrufen von Elementen beliebiger Objekte : itemgetter. Versuchen Sie dies stattdessen:

from operator import itemgetter
x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
sorted(x, key=itemgetter('name'))
Owen
quelle
19

Verwenden der Schwartzschen Transformation von Perl,

py = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

tun

sort_on = "name"
decorated = [(dict_[sort_on], dict_) for dict_ in py]
decorated.sort()
result = [dict_ for (key, dict_) in decorated]

gibt

>>> result
[{'age': 10, 'name': 'Bart'}, {'age': 39, 'name': 'Homer'}]

Mehr zur Perl-Schwartz-Transformation

In der Informatik ist die Schwartzsche Transformation eine Perl-Programmiersprache, mit der die Effizienz beim Sortieren einer Liste von Elementen verbessert wird. Diese Redewendung eignet sich für eine vergleichsbasierte Sortierung, wenn die Reihenfolge tatsächlich auf der Reihenfolge einer bestimmten Eigenschaft (des Schlüssels) der Elemente basiert, wobei die Berechnung dieser Eigenschaft eine intensive Operation ist, die nur minimal ausgeführt werden sollte. Die Schwartzsche Transformation zeichnet sich dadurch aus, dass sie keine benannten temporären Arrays verwendet.

kiriloff
quelle
9
Python hat die Unterstützung key=für .sortseit 2.4, das Jahr 2004 ist, ist es der Schwartzian innerhalb des Sortier Code umwandeln, in C; Daher ist diese Methode nur für Pythons 2.0-2.3 nützlich. Alle sind älter als 12 Jahre.
Antti Haapala
12

irgendwann müssen wir lower()zum Beispiel verwenden

lists = [{'name':'Homer', 'age':39},
  {'name':'Bart', 'age':10},
  {'name':'abby', 'age':9}]

lists = sorted(lists, key=lambda k: k['name'])
print(lists)
# [{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}, {'name':'abby', 'age':9}]

lists = sorted(lists, key=lambda k: k['name'].lower())
print(lists)
# [ {'name':'abby', 'age':9}, {'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]
uingtea
quelle
11

Hier ist die alternative allgemeine Lösung: Sie sortiert Elemente des Diktats nach Schlüsseln und Werten. Der Vorteil davon - keine Notwendigkeit, Schlüssel anzugeben, und es würde immer noch funktionieren, wenn einige Schlüssel in einigen Wörterbüchern fehlen.

def sort_key_func(item):
    """ helper function used to sort list of dicts

    :param item: dict
    :return: sorted list of tuples (k, v)
    """
    pairs = []
    for k, v in item.items():
        pairs.append((k, v))
    return sorted(pairs)
sorted(A, key=sort_key_func)
vvladymyrov
quelle
10

Die Verwendung des Pandas-Pakets ist eine andere Methode, obwohl die Laufzeit im großen Maßstab viel langsamer ist als die traditionelleren Methoden, die von anderen vorgeschlagen wurden:

import pandas as pd

listOfDicts = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
df = pd.DataFrame(listOfDicts)
df = df.sort_values('name')
sorted_listOfDicts = df.T.to_dict().values()

Hier sind einige Benchmark-Werte für eine winzige Liste und eine große Liste (über 100.000) von Diktaten:

setup_large = "listOfDicts = [];\
[listOfDicts.extend(({'name':'Homer', 'age':39}, {'name':'Bart', 'age':10})) for _ in range(50000)];\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(listOfDicts);"

setup_small = "listOfDicts = [];\
listOfDicts.extend(({'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}));\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(listOfDicts);"

method1 = "newlist = sorted(listOfDicts, key=lambda k: k['name'])"
method2 = "newlist = sorted(listOfDicts, key=itemgetter('name')) "
method3 = "df = df.sort_values('name');\
sorted_listOfDicts = df.T.to_dict().values()"

import timeit
t = timeit.Timer(method1, setup_small)
print('Small Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_small)
print('Small Method LC2: ' + str(t.timeit(100)))
t = timeit.Timer(method3, setup_small)
print('Small Method Pandas: ' + str(t.timeit(100)))

t = timeit.Timer(method1, setup_large)
print('Large Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_large)
print('Large Method LC2: ' + str(t.timeit(100)))
t = timeit.Timer(method3, setup_large)
print('Large Method Pandas: ' + str(t.timeit(1)))

#Small Method LC: 0.000163078308105
#Small Method LC2: 0.000134944915771
#Small Method Pandas: 0.0712950229645
#Large Method LC: 0.0321750640869
#Large Method LC2: 0.0206089019775
#Large Method Pandas: 5.81405615807
abby sobh
quelle
3
Ich habe Ihren Code ausgeführt und einen Fehler in der timeit.Timer-Argumentation für Pandas mit großer Methode gefunden: Sie geben "setup_small" an, wobei es "setup_large" sein sollte. Das Ändern dieses Args führte dazu, dass das Programm ohne Abschluss ausgeführt wurde, und ich stoppte es nach mehr als 5 Minuten. Als ich es mit "timeit (1)" ausführte, endeten die Pandas mit großer Methode in 7,3 Sekunden, viel schlimmer als LC oder LC2.
Clp2
Du hast ganz recht, das war ein ziemliches Versehen von meiner Seite. Ich empfehle es nicht mehr für große Fälle! Ich habe die Antwort bearbeitet, um sie einfach als Möglichkeit zuzulassen. Der Anwendungsfall steht noch zur Debatte.
Abby Sobh
6

Wenn Sie das Original listvon nicht benötigen dictionaries, können Sie es sort()mithilfe einer benutzerdefinierten Tastenfunktion direkt mit der Methode ändern .

Schlüsselfunktion:

def get_name(d):
    """ Return the value of a key in a dictionary. """

    return d["name"]

Die listzu sortierenden:

data_one = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]

Sortieren an Ort und Stelle:

data_one.sort(key=get_name)

Wenn Sie das Original benötigen list, rufen Sie die sorted()Funktion auf, die es listund die Schlüsselfunktion übergibt, und weisen Sie die zurückgegebene Sortierung listeiner neuen Variablen zu:

data_two = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]
new_data = sorted(data_two, key=get_name)

Drucken data_oneund new_data.

>>> print(data_one)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
>>> print(new_data)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
Srisaila
quelle
6

Angenommen, ich habe ein Wörterbuch Dmit Elementen unten. Verwenden Sie zum Sortieren einfach das Schlüsselargument in sortiert, um die benutzerdefinierte Funktion wie folgt zu übergeben:

D = {'eggs': 3, 'ham': 1, 'spam': 2}
def get_count(tuple):
    return tuple[1]

sorted(D.items(), key = get_count, reverse=True)
# or
sorted(D.items(), key = lambda x: x[1], reverse=True)  # avoiding get_count function call

Überprüfen Sie dies heraus.

Shank_Transformer
quelle
3

Ich war ein großer Fan von Filter mit Lambda, aber es ist nicht die beste Option, wenn man die Komplexität der Zeit berücksichtigt

Erste Wahl

sorted_list = sorted(list_to_sort, key= lambda x: x['name'])
# returns list of values

Zweite Option

list_to_sort.sort(key=operator.itemgetter('name'))
#edits the list, does not return a new list

Schneller Vergleich der Ausführungszeiten

# First option
python3.6 -m timeit -s "list_to_sort = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}, {'name':'Faaa', 'age':57}, {'name':'Errr', 'age':20}]" -s "sorted_l=[]" "sorted_l = sorted(list_to_sort, key=lambda e: e['name'])"

1000000 Schleifen, am besten 3: 0,736 usec pro Schleife

# Second option 
python3.6 -m timeit -s "list_to_sort = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}, {'name':'Faaa', 'age':57}, {'name':'Errr', 'age':20}]" -s "sorted_l=[]" -s "import operator" "list_to_sort.sort(key=operator.itemgetter('name'))"

1000000 Schleifen, am besten 3: 0,438 usec pro Schleife

Bejür
quelle
2

Wenn die Leistung ein Problem darstellt, würde ich operator.itemgetterstattdessen verwenden, lambdada integrierte Funktionen schneller ausgeführt werden als handgefertigte Funktionen. Die itemgetterFunktion scheint ungefähr 20% schneller zu sein als lambdanach meinen Tests.

Von https://wiki.python.org/moin/PythonSpeed :

Ebenso laufen die eingebauten Funktionen schneller als handgefertigte Äquivalente. Beispielsweise ist map (operator.add, v1, v2) schneller als map (Lambda x, y: x + y, v1, v2).

Hier ist ein Vergleich Geschwindigkeit des Sortierens mit lambdavs itemgetter.

import random
import operator

# create a list of 100 dicts with random 8-letter names and random ages from 0 to 100.
l = [{'name': ''.join(random.choices(string.ascii_lowercase, k=8)), 'age': random.randint(0, 100)} for i in range(100)]

# Test the performance with a lambda function sorting on name
%timeit sorted(l, key=lambda x: x['name'])
13 µs ± 388 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

# Test the performance with itemgetter sorting on name
%timeit sorted(l, key=operator.itemgetter('name'))
10.7 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

# Check that each technique produces same sort order
sorted(l, key=lambda x: x['name']) == sorted(l, key=operator.itemgetter('name'))
True

Beide Techniken sortieren die Liste in derselben Reihenfolge (überprüft durch Ausführung der endgültigen Anweisung im Codeblock), aber eine ist etwas schneller.

swac
quelle
-1

Sie können den folgenden Code verwenden

sorted_dct = sorted(dct_name.items(), key = lambda x : x[1])
Loochie
quelle