Bagaimana cara saya mengurutkan daftar kamus berdasarkan nilai kamus?

1899

Saya memiliki daftar kamus dan ingin setiap item diurutkan berdasarkan nilai properti tertentu.

Pertimbangkan array di bawah ini,

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

Ketika diurutkan berdasarkan name, harus menjadi

[{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]
masi
sumber
Baca jawabannya dan cari di operator.itemgetter . Dapatkah saya mengurutkan beberapa nilai dalam proses yang sama (misalnya, kami memiliki [{'name':'Bart', 'age':10, 'note':3},{'name':'Homer','age':10,'note':2},{'name':'Vasile','age':20,'note':3}] dan menggunakan: from operator import itemgetter newlist = sorted(old_list, key=itemgetter(-'note','name') EDIT: Diuji, dan itu berfungsi tetapi saya tidak tahu cara membuat DESC catatan dan nama ASC.
Claudiu

Jawaban:

2471

Ini mungkin terlihat lebih bersih menggunakan kunci bukan cmp:

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

atau seperti yang disarankan JFSebastian dan lainnya,

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

Untuk kelengkapan (seperti yang ditunjukkan dalam komentar oleh fitzgeraldsteele), tambahkan reverse=Trueuntuk mengurutkan menurun

newlist = sorted(l, key=itemgetter('name'), reverse=True)
Mario F
sumber
34
Menggunakan kunci tidak hanya lebih bersih tetapi juga lebih efisien.
jfs
5
Cara tercepat adalah dengan menambahkan pernyataan newlist.reverse (). Kalau tidak, Anda dapat mendefinisikan perbandingan seperti cmp = lambda x, y: - cmp (x ['nama'], y ['nama']).
Mario F
3
jika nilai sortirnya adalah angka, Anda bisa mengatakan: lambda k: (k ['age'] * -1) untuk mendapatkan sortir terbalik
Philluminati
2
Ini juga berlaku untuk daftar tupel, jika Anda menggunakan di itemgetter(i)mana iindeks elemen tuple untuk mengurutkan.
radicand
42
itemgettermenerima lebih dari satu argumen: itemgetter(1,2,3)adalah fungsi yang mengembalikan tuple like obj[1], obj[2], obj[3], sehingga Anda dapat menggunakannya untuk melakukan jenis yang kompleks.
Bakuriu
167
import operator

Untuk mengurutkan daftar kamus dengan kunci = 'nama':

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

Untuk mengurutkan daftar kamus berdasarkan key = 'age':

list_of_dicts.sort(key=operator.itemgetter('age'))
cedbeu
sumber
9
Pokoknya untuk menggabungkan nama dan umur? (seperti dalam SQL ORDER DENGAN nama, umur?)
monojohnny
28
@onojohnny: ya, kunci saja mengembalikan tuple key=lambda k: (k['name'], k['age']),. (atau key=itemgetter('name', 'age')). tuple cmpakan membandingkan setiap elemen secara bergantian. sangat brilian.
Claudiu
1
Dalam dokumentasi ( docs.python.org/2/tutorial/datastructures.html ) keyargumen opsional untuk list.sort()tidak dijelaskan. Ada ide di mana menemukannya?
TTT
2
@TTT: Lihat dokumentasi perpustakaan untuk listdan teman-teman.
Kevin
65
my_list = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

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

my_list sekarang akan menjadi apa yang Anda inginkan.

(3 tahun kemudian) Diedit untuk menambahkan:

keyArgumen baru lebih efisien dan lebih rapi. Jawaban yang lebih baik sekarang terlihat seperti:

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

... lambda adalah, IMO, lebih mudah dipahami daripada operator.itemgetter, tetapi YMMV.

pjz
sumber
51

Jika Anda ingin mengurutkan daftar dengan beberapa tombol, Anda dapat melakukan hal berikut:

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']))

Ini agak meretas, karena bergantung pada konversi nilai menjadi representasi string tunggal untuk perbandingan, tetapi berfungsi seperti yang diharapkan untuk angka termasuk yang negatif (meskipun Anda perlu memformat string Anda dengan tepat dengan bantalan nol jika Anda menggunakan angka)

Dologan
sumber
2
diurutkan menggunakan timsort yang stabil, Anda dapat memanggil diurutkan beberapa kali untuk mengurutkan pada beberapa kriteria
njzk2
Komentar njzk2 tidak segera jelas bagi saya jadi saya menemukan yang berikut ini. Anda bisa mengurutkan dua kali seperti yang disarankan njzk2, atau meneruskan beberapa argumen ke operator.itemgetter di jawaban atas. Tautan: stackoverflow.com/questions/5212870/…
Permafacture
15
Tidak perlu dikonversi ke string. Kembalikan tuple sebagai kuncinya.
Winston Ewert
Mengurutkan beberapa kali adalah solusi umum termudah tanpa peretasan: stackoverflow.com/a/29849371/1805397
wouter bolsterlee
30
import operator
a_list_of_dicts.sort(key=operator.itemgetter('name'))

'key' digunakan untuk mengurutkan berdasarkan nilai sewenang-wenang dan 'itemgetter' menetapkan nilai itu ke atribut 'nama' setiap item.

efotinis
sumber
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
sumber
21

Saya kira Anda maksud:

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

Ini akan disortir seperti ini:

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

Anda bisa menggunakan fungsi perbandingan khusus, atau Anda bisa meneruskan fungsi yang menghitung kunci penyortiran khusus. Itu biasanya lebih efisien karena kuncinya hanya dihitung sekali per item, sedangkan fungsi perbandingan akan dipanggil berkali-kali.

Anda bisa melakukannya dengan cara ini:

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

Tapi perpustakaan standar berisi rutin generik untuk mendapatkan barang-barang dari objek sewenang-wenang: itemgetter. Jadi coba ini sebagai gantinya:

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

Menggunakan Schwartzian transform dari Perl,

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

melakukan

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

memberi

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

Lebih lanjut tentang transformasi Perl Schwartzian

Dalam ilmu komputer, transformasi Schwartzian adalah idiom pemrograman Perl yang digunakan untuk meningkatkan efisiensi penyortiran daftar item. Ungkapan ini sesuai untuk penyortiran berbasis perbandingan ketika pemesanan sebenarnya didasarkan pada pemesanan properti tertentu (kunci) dari elemen, di mana menghitung properti itu adalah operasi intensif yang harus dilakukan beberapa kali. Transformasi Schwartzian terkenal karena tidak menggunakan array sementara bernama.

Kiriloff
sumber
9
Python telah mendukung key=untuk .sortsejak 2.4, yaitu tahun 2004, ia melakukan transformasi Schwartzian dalam kode penyortiran, di C; dengan demikian metode ini hanya berguna pada Python 2.0-2.3. yang semuanya berusia lebih dari 12 tahun.
Antti Haapala
12

kadang-kadang kita perlu menggunakan lower()misalnya

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
sumber
11

Inilah solusi umum alternatif - ini mengurutkan elemen-elemen dict dengan kunci dan nilai. Keuntungannya - tidak perlu menentukan kunci, dan itu akan tetap berfungsi jika beberapa kunci tidak ada di beberapa kamus.

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
sumber
10

Menggunakan paket panda adalah metode lain, meskipun runtime dalam skala besar jauh lebih lambat daripada metode yang lebih tradisional yang diusulkan oleh orang lain:

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()

Berikut adalah beberapa nilai patokan untuk daftar kecil dan daftar besar (100k +) daftar berikut:

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
sumber
3
Saya menjalankan kode Anda dan menemukan kesalahan di timeit.Timer berpendapat untuk Large Method Pandas: Anda menentukan "setup_small" di mana seharusnya "setup_large". Mengubah argumen itu menyebabkan program berjalan tanpa menyelesaikan, dan saya menghentikannya setelah lebih dari 5 menit. Ketika saya menjalankannya dengan "timeit (1)", Panda Metode Besar selesai dalam 7,3 detik, jauh lebih buruk daripada LC atau LC2.
clp2
Anda benar, itu adalah kekhilafan saya. Saya tidak lagi merekomendasikannya untuk kasus besar! Saya telah mengedit jawaban untuk membiarkannya sebagai kemungkinan, use case masih bisa diperdebatkan.
abby sobh
6

Jika Anda tidak perlu asli listdari dictionaries, Anda bisa memodifikasinya di tempat dengan sort()metode menggunakan fungsi tombol kustom.

Fungsi kunci:

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

    return d["name"]

Yang listakan diurutkan:

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

Mengurutkannya di tempat:

data_one.sort(key=get_name)

Jika Anda memerlukan yang asli list, panggil sorted()fungsi yang meneruskannya listdan fungsi tombol, lalu tetapkan yang dikembalikan yang diurutkan listke variabel baru:

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

Pencetakan data_onedan 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
sumber
6

Katakanlah saya memiliki kamus Ddengan elemen di bawah ini. Untuk mengurutkan cukup gunakan argumen kunci diurutkan untuk melewati fungsi kustom seperti di bawah ini:

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

Lihat ini .

Shank_Transformer
sumber
3

Saya telah menjadi penggemar berat filter w / lambda namun itu bukan pilihan terbaik jika Anda mempertimbangkan kompleksitas waktu

Pilihan pertama

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

Opsi kedua

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

Perbandingan cepat dari waktu exec

# 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'])"

10.00000 loop, terbaik 3: 0,736 usec per loop

# 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'))"

10.00000 loop, terbaik 3: 0,438 usec per loop

Bejür
sumber
2

Jika kinerja menjadi masalah, saya akan menggunakan operator.itemgetteralih-alih lambdafungsi bawaan berfungsi lebih cepat daripada fungsi buatan tangan. The itemgetterFungsi tampaknya melakukan sekitar 20% lebih cepat dari lambdaberdasarkan pengujian saya.

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

Demikian juga, fungsi builtin berjalan lebih cepat daripada setara buatan tangan. Misalnya, peta (operator.add, v1, v2) lebih cepat dari peta (lambda x, y: x + y, v1, v2).

Berikut ini adalah perbandingan menyortir kecepatan menggunakan 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

Kedua teknik mengurutkan daftar dalam urutan yang sama (diverifikasi oleh pelaksanaan pernyataan akhir dalam blok kode) tetapi satu sedikit lebih cepat.

swac
sumber
-1

Anda dapat menggunakan kode berikut

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