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

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

Примите во внимание массив ниже,

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

Когда отсортировано name, должно стать

[{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]
16.09.2008 14:27:47
18 ОТВЕТОВ
РЕШЕНИЕ

Это может выглядеть чище, используя ключ вместо cmp:

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

или, как предложил JFSebastian и другие,

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

Для полноты (как указано в комментариях fitzgeraldsteele) добавьте reverse=Trueсортировку по убыванию

newlist = sorted(l, key=itemgetter('name'), reverse=True)
2422
31.12.2014 19:39:49
Использование ключа не только чище, но и более эффективно.
jfs 16.09.2008 15:03:43
Самый быстрый способ - добавить оператор newlist.reverse (). В противном случае вы можете определить сравнение, например, cmp = lambda x, y: - cmp (x ['name'], y ['name']).
Mario F 13.10.2009 07:14:38
если значение сортировки - это число, вы можете сказать: lambda k: (k ['age'] * -1), чтобы получить обратную сортировку
Philluminati 20.11.2009 15:16:18
Это также относится к списку кортежей, если вы используете itemgetter(i)где iиндекс элемента кортежа для сортировки.
radicand 11.07.2012 23:14:38
itemgetterпринимает более одного аргумента: itemgetter(1,2,3)это функция, которая возвращает подобный кортежу obj[1], obj[2], obj[3], поэтому вы можете использовать его для выполнения сложных сортировок.
Bakuriu 7.09.2012 17:59:06

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

18
16.09.2008 14:31:52

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

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

Это будет отсортировано так:

sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))
21
16.09.2008 14:36:54
my_list = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

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

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

(3 года спустя) Отредактировано, чтобы добавить:

Новый keyаргумент более эффективен и аккуратен. Лучший ответ теперь выглядит так:

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

... лямбда, IMO, легче понять, чем operator.itemgetter, но YMMV.

63
6.06.2016 13:42:15
import operator
a_list_of_dicts.sort(key=operator.itemgetter('name'))

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

30
16.09.2008 14:43:51

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

Вы можете сделать это следующим образом:

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'))
19
16.09.2008 14:52:14
import operator

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

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

Чтобы отсортировать список словарей по ключу = 'возраст':

list_of_dicts.sort(key=operator.itemgetter('age'))
162
1.05.2013 14:42:05
В любом случае, чтобы объединить имя и возраст? (как в SQL ORDER BY имя, возраст?)
monojohnny 17.02.2010 13:10:57
@monojohnny: да, просто пусть ключ вернет кортеж key=lambda k: (k['name'], k['age']). (или key=itemgetter('name', 'age')). кортежи cmpбудут сравнивать каждый элемент по очереди. это чертовски блестяще.
Claudiu 4.09.2013 22:21:15
В документации ( docs.python.org/2/tutorial/datastructures.html ) необязательный keyаргумент for list.sort()не описан. Есть идеи, где это найти?
TTT 21.02.2014 15:21:55
@TTT: см. Документацию библиотеки для listдрузей.
Kevin 19.02.2015 14:56:53

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

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

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

50
10.04.2013 11:31:15
отсортированный с помощью timsort, который является стабильным, вы можете вызвать сортировку несколько раз, чтобы выполнить сортировку по нескольким критериям
njzk2 29.05.2013 13:41:47
Комментарий njzk2 не был сразу понятен мне, поэтому я нашел следующее. Вы можете просто отсортировать дважды, как предлагает njzk2, или передать несколько аргументов operator.itemgetter в верхнем ответе. Ссылка: stackoverflow.com/questions/5212870/…
Permafacture 23.08.2013 21:05:24
Нет необходимости конвертировать в строку. Просто верните кортеж в качестве ключа.
Winston Ewert 15.12.2013 04:55:55
Сортировка несколько раз - самое простое универсальное решение без хаков: stackoverflow.com/a/29849371/1805397
wouter bolsterlee 24.04.2015 13:59:58

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

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

делать

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 Schwartzian

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

19
27.05.2013 11:21:03
Python поддерживает key=for .sortс 2.4, то есть с 2004 года, он выполняет преобразование Шварца в коде сортировки в C; таким образом, этот метод полезен только на Pythons 2.0-2.3. всем из которых более 12 лет.
Antti Haapala 15.02.2015 20:11:29

Допустим, у меня есть словарь 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

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

6
18.06.2019 17:21:31

Вот альтернативное общее решение - оно сортирует элементы 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)
11
22.01.2015 17:21:17

Использование пакета 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
10
9.11.2016 16:58:57
Я запустил ваш код и обнаружил ошибку в аргументах timeit.Timer для больших панд: вы указываете «setup_small», где он должен быть «setup_large». Изменение этого аргумента привело к тому, что программа запустилась без завершения, и я остановил ее более чем через 5 минут. Когда я запустил «timeit (1)», Панды Большого Метода закончили за 7,3 секунды, что намного хуже, чем LC или LC2.
clp2 7.11.2016 04:05:26
Вы совершенно правы, это было довольно упущением с моей стороны. Я больше не рекомендую это для больших случаев! Я отредактировал ответ, чтобы просто допустить его как возможность, вариант использования все еще обсуждается.
abby sobh 8.11.2016 22:58:51
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']) 
27
17.03.2017 10:29:50

когда-нибудь нам нужно использовать, 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}]
12
14.07.2017 03:21:08

Если вам не нужен оригинал 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()функцию, передающую ему функцию listand и key, а затем присвойте возвращенный отсортированный 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}]
6
19.12.2017 17:31:30

Вы можете использовать следующий код

sorted_dct = sorted(dct_name.items(), key = lambda x : x[1])
-1
30.11.2018 09:40:11

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

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

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, 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 мксек на петлю

3
1.10.2019 07:24:42

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

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

Аналогично, встроенные функции работают быстрее, чем созданные вручную эквиваленты. Например, map (operator.add, v1, v2) быстрее, чем map (лямбда 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 same sort order
sorted(l, key=lambda x: x['name']) == sorted(l, key=operator.itemgetter('name'))
True

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

1
18.01.2020 16:32:05