Odpowiedzi:
Może wyglądać na czystsze za pomocą klucza zamiast cmp:
newlist = sorted(list_to_be_sorted, key=lambda k: k['name'])
lub jak sugerowali JFSebastian i inni,
from operator import itemgetter
newlist = sorted(list_to_be_sorted, key=itemgetter('name'))
Dla kompletności (jak wskazano w komentarzach fitzgeraldsteele), dodaj reverse=True
do sortowania malejąco
newlist = sorted(l, key=itemgetter('name'), reverse=True)
itemgetter(i)
gdzie i
jest indeks elementu krotki do sortowania.
itemgetter
akceptuje więcej niż jeden argument: itemgetter(1,2,3)
jest funkcją, która zwraca krotkę obj[1], obj[2], obj[3]
, dzięki czemu można jej używać do wykonywania złożonych sortowań.
import operator
Aby posortować listę słowników według klucza = „nazwa”:
list_of_dicts.sort(key=operator.itemgetter('name'))
Aby posortować listę słowników według klucza = „wiek”:
list_of_dicts.sort(key=operator.itemgetter('age'))
key=lambda k: (k['name'], k['age'])
. (lub key=itemgetter('name', 'age')
). krotki cmp
porównują kolejno każdy element. to jest cholernie genialne.
key
argument dla list.sort()
nie jest opisany. Masz pomysł, gdzie to znaleźć?
list
znajomych i przyjaciół.
my_list = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
my_list.sort(lambda x,y : cmp(x['name'], y['name']))
my_list
będzie teraz tym, czego chcesz.
(3 lata później) Edytowano, aby dodać:
Nowy key
argument jest bardziej wydajny i bardziej przejrzysty. Lepsza odpowiedź wygląda teraz:
my_list = sorted(my_list, key=lambda k: k['name'])
... lambda jest, IMO, łatwiejsze do zrozumienia niż operator.itemgetter
, ale YMMV.
Jeśli chcesz posortować listę według wielu kluczy, możesz wykonać następujące czynności:
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']))
Jest to dość hackerskie, ponieważ polega na przekształceniu wartości w reprezentację pojedynczego ciągu w celu porównania, ale działa zgodnie z oczekiwaniami w przypadku liczb, w tym ujemnych (chociaż będziesz musiał odpowiednio sformatować swój ciąg zerami, jeśli używasz liczb)
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'])
Chyba miałeś na myśli:
[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
To byłoby posortowane w następujący sposób:
sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))
Możesz użyć niestandardowej funkcji porównania lub przekazać funkcję, która oblicza niestandardowy klucz sortowania. Zwykle jest to bardziej wydajne, ponieważ klucz jest obliczany tylko raz na pozycję, podczas gdy funkcja porównania byłaby wywoływana wiele razy.
Możesz to zrobić w ten sposób:
def mykey(adict): return adict['name']
x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
sorted(x, key=mykey)
Ale średnia biblioteka zawiera ogólną procedurę dla coraz elementy dowolnych obiektów: itemgetter
. Spróbuj więc zamiast tego:
from operator import itemgetter
x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
sorted(x, key=itemgetter('name'))
Używając transformacji Schwartzian z Perla,
py = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
zrobić
sort_on = "name"
decorated = [(dict_[sort_on], dict_) for dict_ in py]
decorated.sort()
result = [dict_ for (key, dict_) in decorated]
daje
>>> result
[{'age': 10, 'name': 'Bart'}, {'age': 39, 'name': 'Homer'}]
Więcej na temat transformacji Perla Schwartziana
W informatyce transformacja Schwartziana jest idiomem programistycznym Perla, używanym do poprawy wydajności sortowania listy pozycji. Ten idiom jest odpowiedni do sortowania opartego na porównaniu, gdy porządkowanie jest faktycznie oparte na uporządkowaniu określonej właściwości (klucza) elementów, przy czym obliczenie tej właściwości jest intensywną operacją, którą należy wykonać minimalną liczbę razy. Transformacja Schwartziana wyróżnia się tym, że nie używa nazwanych tablic tymczasowych.
key=
na .sort
od 2,4, to jest rok 2004, to robi Transformacja Schwartza w kodzie sortowania w C; dlatego ta metoda jest przydatna tylko w Pythons 2.0-2.3. z których wszystkie mają więcej niż 12 lat.
Musisz wdrożyć własną funkcję porównywania, która będzie porównywać słowniki według wartości kluczy nazw. Zobacz temat Sortowanie mini-JAK Z PythonInfo Wiki
czasami musimy lower()
na przykład użyć
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}]
Oto alternatywne rozwiązanie ogólne - sortuje elementy dict według kluczy i wartości. Zaletą tego jest to, że nie trzeba określać kluczy, a nadal działałoby, gdyby niektórych kluczy brakowało w niektórych słownikach.
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)
Korzystanie z pakietu pand to kolejna metoda, choć jego środowisko uruchomieniowe na dużą skalę jest znacznie wolniejsze niż bardziej tradycyjne metody proponowane przez innych:
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()
Oto kilka wartości porównawczych dla małej listy i dużej (100 tys.) Listy nagrań:
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
Jeśli nie trzeba oryginał list
z dictionaries
, można zmodyfikować go w miejscu z sort()
metodą z użyciem funkcji klawisza zwyczaj.
Klawisz funkcyjny:
def get_name(d):
""" Return the value of a key in a dictionary. """
return d["name"]
Do list
posortowania:
data_one = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]
Sortowanie w miejscu:
data_one.sort(key=get_name)
Jeśli potrzebujesz oryginału list
, wywołaj sorted()
funkcję przekazującą go list
i funkcję klucza, a następnie przypisz zwrócony sort list
do nowej zmiennej:
data_two = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]
new_data = sorted(data_two, key=get_name)
Drukowanie data_one
i new_data
.
>>> print(data_one)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
>>> print(new_data)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
Powiedzmy, że mam słownik D
z elementami poniżej. Aby posortować, użyj posortowanego argumentu w celu przekazania funkcji niestandardowej, jak poniżej:
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
Sprawdź to .
Byłem wielkim fanem filtra w / lambda, jednak nie jest to najlepsza opcja, jeśli wziąć pod uwagę złożoność czasu
Pierwsza opcja
sorted_list = sorted(list_to_sort, key= lambda x: x['name'])
# returns list of values
Druga opcja
list_to_sort.sort(key=operator.itemgetter('name'))
#edits the list, does not return a new list
Szybkie porównanie czasów wykonania
# 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 pętli, najlepiej 3: 0,736 usec na pętlę
# 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 pętli, najlepiej 3: 0,438 usec na pętlę
Jeśli problemem jest wydajność, użyłbym operator.itemgetter
zamiast tego, lambda
ponieważ wbudowane funkcje działają szybciej niż funkcje ręcznie wykonane. itemgetter
Funkcja wydaje się wykonać około 20% szybciej niż lambda
w oparciu o moje badania.
Od https://wiki.python.org/moin/PythonSpeed :
Podobnie, wbudowane funkcje działają szybciej niż ręcznie tworzone odpowiedniki. Na przykład mapa (operator.add, v1, v2) jest szybsza niż mapa (lambda x, y: x + y, v1, v2).
Oto porównanie prędkości sortowania używając lambda
vs 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
Obie techniki sortują listę w tej samej kolejności (weryfikowana przez wykonanie instrukcji końcowej w bloku kodu), ale jedna jest nieco szybsza.
[{'name':'Bart', 'age':10, 'note':3},{'name':'Homer','age':10,'note':2},{'name':'Vasile','age':20,'note':3}]
I do użycia:from operator import itemgetter newlist = sorted(old_list, key=itemgetter(-'note','name')
EDYCJA: Przetestowano i działa, ale nie wiem, jak zrobić notatkę DESC i nazwać ASC.