2013-04-18 4 views
59

Я хочу сначала отсортировать список по значению, а затем по второму значению. Есть простой способ сделать это? Вот небольшой пример:Python: как отсортировать список словарей по нескольким значениям?

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

Эта команда для сортировки этого списка 'name':

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

Но как я могу отсортировать этот список по второму значению? Например, 'age' в этом примере.

Я хочу сортировкой, как это (сортировать по 'name', а затем сортировать по 'age'):

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

Спасибо!

+5

На стороне нет: вроде питона будет * * гарантированно будет стабильным, таким образом, вы можете просто '' sort' по age', а затем 'name', чтобы получить результат, который вы хотели. (обратите внимание, что клавиши находятся в обратном порядке, сначала сортируйте по второму ключу, а затем по первому). – Bakuriu

ответ

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

Это сорта кортежем из двух атрибутов, следующий эквивалент и гораздо быстрее/очиститель:

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

Из комментариев: @Bakuriu

Держу пари, есть не большая разница между ними, но itemgetter избегает немного накладных расходов, потому что извлекает ключи и делает tuple во время одного кода операции (CALL_FUNCTION), вызывая lambda придется вызвать функцию, загрузить различные константы (которые являются другими байткодами), наконец, вызвать индекс (BINARY_SUBSCR), построить tuple и вернуть его ... это гораздо больше работы для интерпретатора.

Резюмируя: itemgetter продолжает выполнение полностью на C уровне, так что это как можно быстрее.

+3

Мне будет интересно узнать, почему * itemgetter будет намного быстрее, чем выражение лямбда. Разве это не сводится к тому же поиску? – catchmeifyoutry

+4

@catchmeifyoutry Я держу пари, что между ними нет * большой * разницы, но 'itemgetter' избегает немного накладных расходов, потому что он извлекает ключи и делает кортеж в течение одного кода операции (' CALL_FUNCTION'), а вызов лямбда нужно вызвать функцию, загрузить различные константы (которые являются другими байткодами), наконец, вызвать индекс ('BINARY_SUBSCR'), построить кортеж и вернуть его ... это намного больше работы для интерпретатора – Bakuriu

+0

@Bakuriu Спасибо, за объяснение. Таким образом, реализация itemgetter оптимизируется в cpython как c-код, а не только как ссылочный код python, упомянутый в его онлайн-документации. – catchmeifyoutry

52
from operator import itemgetter 

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

Вам интересно, что 'operator.itemgetter' делает что-то почти магическое? Это не. В этом случае он возвращает кортеж (длиной в два раза), а затем 'sorted' делает * The Right Thing * с этим. –

0

Это альтернативное общее решение - оно сортирует элементы dict по ключам и значениям. Преимущество этого - нет необходимости указывать ключи, и он все равно будет работать, если некоторые ключи отсутствуют в некоторых словарях.

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) 
Смежные вопросы