Làm cách nào để sắp xếp danh sách từ điển theo giá trị của từ điển?


1898

Tôi có một danh sách từ điển và muốn mỗi mục được sắp xếp theo một giá trị thuộc tính cụ thể.

Hãy xem xét các mảng dưới đây,

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

Khi được sắp xếp theo name, sẽ trở thành

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

Đọc câu trả lời và tìm kiếm trên toán tử.itemgetter . Tôi có thể sắp xếp nhiều giá trị trong cùng một quy trình không (ví dụ: chúng tôi có [{'name':'Bart', 'age':10, 'note':3},{'name':'Homer','age':10,'note':2},{'name':'Vasile','age':20,'note':3}] Và sử dụng: from operator import itemgetter newlist = sorted(old_list, key=itemgetter(-'note','name') EDIT: Đã thử nghiệm và nó đang hoạt động nhưng tôi không biết cách ghi chú DESC và đặt tên ASC.
Claudiu

Câu trả lời:


2470

Nó có thể trông sạch hơn bằng cách sử dụng một phím thay vì cmp:

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

hoặc như JFSebastian và những người khác đề xuất,

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

Để hoàn thiện (như được chỉ ra trong các nhận xét của fitzgeraldsteele), hãy thêm reverse=Truevào sắp xếp giảm dần

newlist = sorted(l, key=itemgetter('name'), reverse=True)

34
Sử dụng chìa khóa không chỉ sạch hơn mà còn hiệu quả hơn.
jfs

5
Cách nhanh nhất sẽ là thêm một câu lệnh newlist.reverse (). Nếu không, bạn có thể định nghĩa một so sánh như cmp = lambda x, y: - cmp (x ['name'], y ['name']).
Mario F

3
nếu giá trị sắp xếp là một số bạn có thể nói: lambda k: (k ['age'] * -1) để có được một sắp xếp ngược lại
Philluminati

2
Điều này cũng áp dụng cho một danh sách các hàng, nếu bạn sử dụng itemgetter(i)ilà chỉ số của phần tử tuple để sắp xếp trên.
radicand

42
itemgetterchấp nhận nhiều hơn một đối số: itemgetter(1,2,3)là một hàm trả về một tuple như thế obj[1], obj[2], obj[3], vì vậy bạn có thể sử dụng nó để thực hiện các sắp xếp phức tạp.
Bakuriu

166
import operator

Để sắp xếp danh sách từ điển theo key = 'name':

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

Để sắp xếp danh sách từ điển theo key = 'age':

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

9
Dù sao để kết hợp tên và tuổi? (như trong SQL ĐẶT HÀNG theo tên, tuổi?)
monojohnny

28
@monojohnny: vâng, chỉ cần có chìa khóa trả lại một tuple key=lambda k: (k['name'], k['age']),. (hoặc key=itemgetter('name', 'age')). tuple cmpsẽ so sánh lần lượt từng yếu tố. đó là máu rực rỡ.
Claudiu

1
Trong tài liệu ( docs.python.org/2/tutorial/datastructures.html ) keyđối số tùy chọn list.sort()không được mô tả. Bất cứ ý tưởng nơi để tìm thấy điều đó?
TTT

2
@TTT: Xem tài liệu thư viện cho listvà bạn bè.
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 bây giờ sẽ là những gì bạn muốn.

(3 năm sau) Đã chỉnh sửa để thêm:

Đối keysố mới là hiệu quả hơn và gọn gàng hơn. Một câu trả lời tốt hơn bây giờ trông như:

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

... lambda là, IMO, dễ hiểu hơn operator.itemgetter, nhưng YMMV.


51

Nếu bạn muốn sắp xếp danh sách theo nhiều khóa, bạn có thể làm như sau:

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

Nó khá là hackish, vì nó phụ thuộc vào việc chuyển đổi các giá trị thành một chuỗi đại diện để so sánh, nhưng nó hoạt động như mong đợi đối với các số bao gồm cả số âm (mặc dù bạn sẽ cần định dạng chuỗi của mình một cách phù hợp với số 0 nếu bạn đang sử dụng số)


2
được sắp xếp bằng timsort ổn định, bạn có thể gọi được sắp xếp nhiều lần để sắp xếp theo một số tiêu chí
njzk2

Nhận xét của njzk2 không rõ ràng ngay lập tức vì vậy tôi đã tìm thấy những điều sau đây. Bạn chỉ có thể sắp xếp hai lần như njzk2 gợi ý hoặc chuyển nhiều đối số cho toán tử.itemgetter trong câu trả lời hàng đầu. Liên kết: stackoverflow.com/questions/5212870/ cấp
Permafacture

15
Không cần phải chuyển đổi thành chuỗi. Chỉ cần trả lại một tuple là chìa khóa.
Winston Ewert

Sắp xếp nhiều lần là giải pháp chung dễ dàng nhất mà không cần hack: stackoverflow.com/a/29849371/1805397
wouter bolsterlee

30
import operator
a_list_of_dicts.sort(key=operator.itemgetter('name'))

'key' được sử dụng để sắp xếp theo một giá trị tùy ý và 'itemgetter' đặt giá trị đó cho thuộc tính 'name' của mỗi mục.


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

21

Tôi đoán bạn có nghĩa là:

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

Điều này sẽ được sắp xếp như thế này:

sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))

19

Bạn có thể sử dụng chức năng so sánh tùy chỉnh hoặc bạn có thể chuyển vào hàm tính toán khóa sắp xếp tùy chỉnh. Điều đó thường hiệu quả hơn vì khóa chỉ được tính một lần cho mỗi mục, trong khi chức năng so sánh sẽ được gọi nhiều lần hơn.

Bạn có thể làm theo cách này:

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

Nhưng thư viện chuẩn chứa một thói quen chung để lấy các mục của các đối tượng tùy ý : itemgetter. Vì vậy, hãy thử điều này thay thế:

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

19

Sử dụng biến đổi Schwartzian từ Perl,

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

làm

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

cho

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

Thông tin thêm về biến đổi Perl Schwartzian

Trong khoa học máy tính, biến đổi Schwartzian là một thành ngữ lập trình Perl được sử dụng để cải thiện hiệu quả của việc sắp xếp danh sách các mục. Thành ngữ này phù hợp để sắp xếp dựa trên so sánh khi thứ tự thực sự dựa trên thứ tự của một thuộc tính nhất định (khóa) của các phần tử, trong đó tính toán thuộc tính đó là một hoạt động chuyên sâu nên được thực hiện với số lần tối thiểu. Biến đổi Schwartzian đáng chú ý ở chỗ nó không sử dụng các mảng tạm thời được đặt tên.


9
Python đã ủng hộ key=cho .sorttừ 2.4, đó là năm 2004, nó là Schwartzian chuyển đổi trong mã phân loại, trong C; do đó phương pháp này chỉ hữu ích trên Pythons 2.0-2.3. tất cả đều hơn 12 tuổi.
Antti Haapala


12

đôi khi chúng ta cần sử dụng lower()ví dụ

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}]

11

Đây là giải pháp chung thay thế - nó sắp xếp các yếu tố của dict theo khóa và giá trị. Ưu điểm của nó - không cần chỉ định khóa và nó vẫn hoạt động nếu thiếu một số khóa trong một số từ điển.

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)

10

Sử dụng gói gấu trúc là một phương pháp khác, mặc dù thời gian chạy ở quy mô lớn chậm hơn nhiều so với các phương pháp truyền thống được đề xuất bởi người khác:

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

Dưới đây là một số giá trị điểm chuẩn cho một danh sách nhỏ và một danh sách lớn (100k +) dicts:

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

3
Tôi đã chạy mã của bạn và tìm thấy một lỗi trong timeit.Timer lập luận cho Pandas Phương thức lớn: bạn chỉ định "setup_small" trong đó nên là "setup_large". Thay đổi arg đó khiến chương trình chạy mà không hoàn thành, và tôi đã dừng nó sau hơn 5 phút. Khi tôi chạy nó với "timeit (1)", Pandas Phương thức lớn kết thúc sau 7,3 giây, tệ hơn nhiều so với LC hoặc LC2.
clp2

Bạn hoàn toàn đúng, đó là một sự giám sát khá tốt từ phía tôi. Tôi không còn giới thiệu nó cho các trường hợp lớn! Tôi đã chỉnh sửa câu trả lời để đơn giản cho phép nó như một khả năng, trường hợp sử dụng vẫn còn đang tranh luận.
abby sobh

6

Nếu bạn không cần bản gốc listcủa dictionaries, bạn có thể sửa đổi nó tại chỗ bằng sort()phương thức sử dụng chức năng khóa tùy chỉnh.

Chức năng chính:

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

    return d["name"]

Các listđể được sắp xếp:

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

Sắp xếp nó tại chỗ:

data_one.sort(key=get_name)

Nếu bạn cần bản gốc list, hãy gọi sorted()hàm chuyển qua nó listvà hàm key, sau đó gán hàm được trả về được sắp xếp listcho một biến mới:

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

In ấn data_onenew_data.

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

6

Hãy nói rằng tôi có một từ điển Dvới các yếu tố dưới đây. Để sắp xếp chỉ cần sử dụng đối số khóa trong sắp xếp để truyền chức năng tùy chỉnh như dưới đây:

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

Kiểm tra này .


3

Tôi đã là một fan hâm mộ lớn của bộ lọc w / lambda tuy nhiên đó không phải là lựa chọn tốt nhất nếu bạn xem xét độ phức tạp của thời gian

Lựa chọn đầu tiên

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

Sự lựa chọn thứ hai

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

So sánh nhanh thời gian thực hiện

# 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 vòng, tốt nhất là 3: 0,736 usec mỗi vòng

# 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 vòng, tốt nhất là 3: 0,438 usec mỗi vòng


2

Nếu hiệu suất là một mối quan tâm, tôi sẽ sử dụng operator.itemgetterthay vì các lambdahàm tích hợp thực hiện nhanh hơn các chức năng thủ công. Các itemgetterchức năng dường như để thực hiện khoảng 20% nhanh hơn lambdadựa trên thử nghiệm của tôi.

Từ https://wiki.python.org/moin/PythonSpeed :

Tương tự, các hàm dựng sẵn chạy nhanh hơn các hàm tương đương được làm bằng tay. Ví dụ: map (oper.add, v1, v2) nhanh hơn map (lambda x, y: x + y, v1, v2).

Dưới đây là một so sánh phân loại tốc độ sử dụng 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

Cả hai kỹ thuật sắp xếp danh sách theo cùng một thứ tự (được xác minh bằng cách thực hiện câu lệnh cuối cùng trong khối mã) nhưng một kỹ thuật nhanh hơn một chút.


-1

Bạn có thể sử dụng mã sau đây

sorted_dct = sorted(dct_name.items(), key = lambda x : x[1])
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.