Как отсортировать список словарей по значению словаря?

У меня есть список словарей, и я хочу, чтобы каждый элемент был отсортирован по определенному значению.

Обратите внимание на список:

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

При сортировке по name он должен выглядеть следующим образом:

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

person Community    schedule 16.09.2008    source источник
comment
Чтение ответа и просмотр operator.itemgetter. Могу ли я отсортировать по нескольким значениям в одном процессе (например, у нас есть [{'name':'Bart', 'age':10, 'note':3},{'name':'Homer','age':10,'note':2},{'name':'Vasile','age':20,'note':3}] И для использования: from operator import itemgetter newlist = sorted(old_list, key=itemgetter(-'note','name') РЕДАКТИРОВАТЬ: Протестировано, и он работает, но я не знаю, как записать DESC и назвать ASC.   -  person Claudiu    schedule 21.05.2020


Ответы (16)


Функция sorted() принимает параметр key=

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

Кроме того, вы можете использовать operator.itemgetter вместо того, чтобы определять функцию самостоятельно.

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

Для полноты добавьте reverse=True для сортировки в порядке убывания

newlist = sorted(l, key=itemgetter('name'), reverse=True)
person Mario F    schedule 16.09.2008
comment
Использование клавиш не только чище, но и эффективнее. - person jfs; 16.09.2008
comment
Самый быстрый способ - добавить оператор newlist.reverse (). В противном случае вы можете определить сравнение, например, cmp = lambda x, y: - cmp (x ['name'], y ['name']). - person Mario F; 13.10.2009
comment
если значением сортировки является число, вы можете сказать: лямбда k: (k ['age'] * -1), чтобы получить обратную сортировку - person Philluminati; 20.11.2009
comment
Это также относится к списку кортежей, если вы используете itemgetter(i), где i - это индекс элемента кортежа для сортировки. - person radicand; 12.07.2012
comment
itemgetter принимает более одного аргумента: itemgetter(1,2,3) - это функция, которая возвращает кортеж, например obj[1], obj[2], obj[3], поэтому вы можете использовать ее для выполнения сложных сортировок. - person Bakuriu; 07.09.2012
comment
в случае строк юникода import locale / locale.setlocale(locale.LC_ALL, "") / newlist = sorted(list_to_be_sorted, key=lambda k: locale.strxfrm(k['name'])) работает на основе stackoverflow.com/questions/1097908/ - person webenformasyon; 15.10.2019
comment
что, если мы не знаем значение ключа? - person sattva_venu; 28.01.2021

import operator

Чтобы отсортировать список словарей по key = 'name':

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

Чтобы отсортировать список словарей по key = 'age':

list_of_dicts.sort(key=operator.itemgetter('age'))
person Community    schedule 16.09.2008
comment
Все равно совместить имя и возраст? (как в SQL ORDER BY по имени, возрасту?) - person monojohnny; 17.02.2010
comment
@monojohnny: да, пусть ключ возвращает кортеж, key=lambda k: (k['name'], k['age']). (или key=itemgetter('name', 'age')). cmp кортежа будет сравнивать каждый элемент по очереди. это чертовски здорово. - person Claudiu; 05.09.2013
comment
В документации (docs.python.org/2/tutorial/datastructures.html) необязательный аргумент key для list.sort() не описывается. Есть идеи, где это найти? - person TTT; 21.02.2014
comment
@TTT: см. документацию по библиотеке для list и друзья. - person Kevin; 19.02.2015

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

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

my_list теперь будет тем, чем вы хотите.

Или лучше:

Начиная с Python 2.4, аргумент key стал более эффективным и аккуратным:

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

... лямбда, ИМО, легче понять, чем operator.itemgetter, но ваш пробег может отличаться.

person pjz    schedule 16.09.2008
comment
Что можно сделать, если ключ неизвестен и продолжает меняться? Я имею в виду список dicts с одним ключом и значением, но ключ и значение не могут быть определены, поскольку они продолжают меняться. - person Sam; 01.12.2020
comment
Мне нужен еще один пример. Попробуйте отправить возможное решение на stackexchange codereview и спросите, есть ли способ лучше. - person pjz; 30.12.2020
comment
@Sam, если вы хотите отсортировать по значению единственного ключа в dict, даже если вы не знаете ключ, вы можете сделать key=lambda k: list(k.values())[0] - person pjz; 10.03.2021

Если вы хотите отсортировать список по нескольким ключам, вы можете сделать следующее:

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

Это довольно хакерский метод, поскольку он полагается на преобразование значений в одно строковое представление для сравнения, но он работает, как ожидалось, для чисел, включая отрицательные (хотя вам нужно будет соответствующим образом отформатировать вашу строку с нулевыми отступами, если вы используете числа).

person Community    schedule 18.05.2010
comment
отсортировано с использованием стабильной временной сортировки, вы можете вызвать сортировку несколько раз, чтобы выполнить сортировку по нескольким критериям - person njzk2; 29.05.2013
comment
Комментарий njzk2 не сразу был мне понятен, поэтому я обнаружил следующее. Вы можете просто отсортировать дважды, как предлагает njzk2, или передать несколько аргументов в operator.itemgetter в верхнем ответе. Ссылка: stackoverflow.com/questions / 5212870 / - person Permafacture; 24.08.2013
comment
Нет необходимости преобразовывать в строку. Просто верните кортеж в качестве ключа. - person Winston Ewert; 15.12.2013
comment
Многократная сортировка - это простейшее универсальное решение без взломов: stackoverflow.com/a/29849371/1805397 - person wouter bolsterlee; 24.04.2015

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

key используется для сортировки по произвольному значению, а itemgetter устанавливает это значение для атрибута name каждого элемента.

person efotinis    schedule 16.09.2008

Я думаю, вы имели в виду:

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

Это будет выглядеть так:

sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))
person Bartosz Radaczyński    schedule 16.09.2008

Вы можете использовать настраиваемую функцию сравнения или передать функцию, которая вычисляет настраиваемый ключ сортировки. Обычно это более эффективно, поскольку ключ вычисляется только один раз для каждого элемента, в то время как функция сравнения будет вызываться много раз.

Вы можете сделать это так:

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

Но стандартная библиотека содержит общую процедуру для получения элементов произвольных объектов: itemgetter. Так что попробуйте вместо этого:

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

Используя преобразование Шварца из Perl,

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

do

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

дает

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

Подробнее о преобразовании Перла Шварца:

В информатике преобразование Шварца - это идиома программирования Perl, используемая для повышения эффективности сортировки списка элементов. Эта идиома подходит для сортировки на основе сравнения, когда порядок фактически основан на упорядочении определенного свойства (ключа) элементов, где вычисление этого свойства является интенсивной операцией, которую следует выполнять минимальное количество раз. Преобразование Шварца примечательно тем, что в нем не используются именованные временные массивы.

person Community    schedule 27.05.2013
comment
Python поддерживает key= для .sort с 2.4, то есть в 2004 году, он выполняет преобразование Шварца в коде сортировки на C; таким образом, этот метод полезен только в Pythons 2.0-2.3. все они старше 12 лет. - person Antti Haapala; 15.02.2015

Вы должны реализовать свою собственную функцию сравнения, которая будет сравнивать словари по значениям ключей имен. См. Мини-руководство по сортировке из PythonInfo Wiki

person Matej    schedule 16.09.2008
comment
Это слишком сильно зависит от ссылки. Можете дать более полный ответ? - person Peter Mortensen; 14.08.2020
comment
Надлежащие ответы уже предоставлены и другими участниками. Не стесняйтесь оставить ссылку или удалить ответ. - person Matej; 17.08.2020

Иногда нам нужно использовать lower(). Например,

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}]
person Community    schedule 14.07.2017
comment
Почему нам нужно использовать lower () в этом случае? - person Peter Mortensen; 14.08.2020

Другой метод - использование пакета Pandas, хотя время его выполнения в больших масштабах медленнее, чем более традиционные методы, предложенные другими:

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

Вот некоторые эталонные значения для крошечного списка и большого (более 100 тыс.) Списков диктовок:

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
person Community    schedule 01.09.2016
comment
Я запустил ваш код и обнаружил ошибку в аргументах timeit.Timer для Large Method Pandas: вы указываете setup_small там, где это должно быть setup_large. Изменение этого аргумента привело к тому, что программа запустилась без завершения, и я остановил ее более чем через 5 минут. Когда я запустил его с timeit (1), Large Method Pandas закончил за 7,3 секунды, что намного хуже, чем LC или LC2. - person clp2; 07.11.2016
comment
Вы совершенно правы, это была моя оплошность. Больше не рекомендую для больших случаев! Я отредактировал ответ, чтобы просто допустить это как возможность, вариант использования все еще обсуждается. - person abby sobh; 09.11.2016

Вот альтернативное общее решение - сортирует элементы 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)
sorted(A, key=sort_key_func)
person Community    schedule 22.01.2015
comment
Что вы подразумеваете под сортировкой элементов словаря по ключам и значениям? Как происходит сортировка? В чем заключаются ценности? - person Peter Mortensen; 14.08.2020

Если вам не нужен исходный list из dictionaries, вы можете изменить его на месте с помощью sort() метода, используя настраиваемую ключевую функцию.

Ключевая функция:

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

    return d["name"]

list для сортировки:

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

Сортировка по месту:

data_one.sort(key=get_name)

Если вам нужен исходный list, вызовите функцию sorted(), передав ей list и ключевую функцию, затем присвойте возвращенный отсортированный list новой переменной:

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

Печать data_one и new_data.

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

Допустим, у меня есть словарь D с указанными ниже элементами. Для сортировки просто используйте ключевой аргумент в sorted, чтобы передать настраиваемую функцию, как показано ниже:

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

Проверьте это.

person Community    schedule 16.04.2014

Я был большим поклонником фильтра с лямбдой. Однако это не лучший вариант, если учесть временную сложность.

Первый вариант

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

Второй вариант

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

Быстрое сравнение времени выполнения

# 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 петель, лучшее из 3: 0,736 мкс на петлю

# 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 циклов, лучшее из 3: 0,438 мкс на цикл

person Community    schedule 01.10.2019

Если производительность вызывает беспокойство, я бы использовал operator.itemgetter вместо lambda, поскольку встроенные функции работают быстрее, чем функции, созданные вручную. По результатам моего тестирования функция itemgetter работает примерно на 20% быстрее, чем lambda.

Из https://wiki.python.org/moin/PythonSpeed:

Точно так же встроенные функции работают быстрее, чем эквиваленты, созданные вручную. Например, map (operator.add, v1, v2) быстрее, чем map (lambda x, y: x + y, v1, v2).

Вот сравнение скорости сортировки с использованием lambda и 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 the same sort order
sorted(l, key=lambda x: x['name']) == sorted(l, key=operator.itemgetter('name'))
True

Оба метода сортируют список в одном и том же порядке (проверяется выполнением последнего оператора в блоке кода), но первый работает немного быстрее.

person Community    schedule 18.01.2020