2013-04-18 45 views
59

Tôi muốn sắp xếp một danh sách lúc đầu bởi một giá trị và sau đó là một giá trị thứ hai. Có cách nào làm dễ hơn không? Dưới đây là một ví dụ nhỏ:Python: Cách sắp xếp danh sách từ điển theo một vài giá trị?

A = [{'name':'john','age':45}, 
    {'name':'andi','age':23}, 
    {'name':'john','age':22}, 
    {'name':'paul','age':35}, 
    {'name':'john','age':21}] 

Lệnh này là để phân loại danh sách này bằng 'name':

sorted(A, key = lambda user: user['name']) 

Nhưng làm thế nào tôi có thể sắp xếp danh sách này bằng một giá trị thứ hai? Giống như 'age' trong ví dụ này.

Tôi muốn có một phân loại như thế này (trước sắp xếp theo 'name' và sau đó sắp xếp bởi 'age'):

andi - 23 
john - 21 
john - 22 
john - 45 
paul - 35 

Cảm ơn!

+5

Về một mặt không: phân loại của python * được đảm bảo * ổn định, do đó bạn có thể chỉ cần 'sắp xếp' theo' tuổi' và sau đó là 'tên' để có được kết quả mong muốn. (lưu ý rằng các phím theo thứ tự ngược lại. trước tiên bạn sắp xếp theo khóa thứ hai và sau đó bằng phím thứ nhất). – Bakuriu

Trả lời

85
>>> A = [{'name':'john','age':45}, 
    {'name':'andi','age':23}, 
    {'name':'john','age':22}, 
    {'name':'paul','age':35}, 
    {'name':'john','age':21}] 
>>> sorted(A, key = lambda user: (user['name'], user['age'])) 
[{'age': 23, 'name': 'andi'}, {'age': 21, 'name': 'john'}, {'age': 22, 'name': 'john'}, {'age': 45, 'name': 'john'}, {'age': 35, 'name': 'paul'}] 

này loại bởi một tuple của hai thuộc tính, sau đây là tương đương và nhanh hơn nhiều/sạch hơn:

>>> from operator import itemgetter 
>>> sorted(A, key=itemgetter('name', 'age')) 
[{'age': 23, 'name': 'andi'}, {'age': 21, 'name': 'john'}, {'age': 22, 'name': 'john'}, {'age': 45, 'name': 'john'}, {'age': 35, 'name': 'paul'}] 

Từ nhận xét: @Bakuriu

Tôi đặt cược có không phải là một sự khác biệt lớn giữa hai, nhưng itemgetter tránh một chút chi phí vì nó chiết xuất các khóa và làm cho tuple trong một opcode đơn (CALL_FUNCTION), trong khi gọi số lambda sẽ phải gọi hàm, tải các hằng số khác nhau (là các bytecode khác) cuối cùng gọi số chỉ số (BINARY_SUBSCR), xây dựng tuple và trả về ... đó là công việc nhiều hơn cho trình thông dịch.

Để tóm tắt: itemgetter duy trì quá trình thực hiện đầy đủ ở mức C, do đó, nhanh nhất có thể.

+3

Tôi sẽ quan tâm đến một lời giải thích * tại sao * itemgetter sẽ nhanh hơn nhiều so với biểu thức lambda. Nó không đun sôi xuống cùng một tra cứu? – catchmeifyoutry

+4

@catchmeifyoutry Tôi đặt cược không có sự khác biệt * lớn * giữa hai, nhưng 'itemgetter' tránh được một chút chi phí vì nó trích xuất các khóa và tạo tuple trong một opcode đơn (' CALL_FUNCTION'), trong khi gọi lambda sẽ phải gọi hàm, tải các hằng số khác nhau (đó là các bytecode khác) cuối cùng gọi hàm subscript ('BINARY_SUBSCR'), xây dựng bộ tuple và trả về nó ... đó là công việc nhiều hơn cho trình thông dịch – Bakuriu

+0

@Bakuriu Cảm ơn, vì giải trình. Vì vậy, việc thực hiện itemgetter được tối ưu hóa trong cpython như mã c, không chỉ được thực hiện như mã python tham chiếu được đề cập trong tài liệu trực tuyến của nó. – catchmeifyoutry

52
from operator import itemgetter 

sorted(your_list, key=itemgetter('name', 'age')) 
+2

Bạn có tự hỏi liệu 'operator.itemgetter' có thực hiện điều gì gần như phép thuật không? Nó không. Nó trả về một tuple (có chiều dài là 2 trong trường hợp này) và sau đó 'sort' làm * The Right Thing * với điều đó. –

0

Đây là giải pháp chung thay thế - nó sắp xếp các yếu tố của dict bằng các khóa và giá trị. Lợi thế của nó - không cần phải chỉ định khóa và nó sẽ vẫn hoạt động nếu một số khóa bị thiếu 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) 
Các vấn đề liên quan