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

avatar
masi
16 сентября 2008 в 14:27
912356
18
2263

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

Учитывать список:

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

При сортировке по name он должен выглядеть так:

[{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]
Источник
Claudiu
21 мая 2020 в 07:13
3

Чтение ответа и просмотр 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.

Ответы (18)

avatar
Mario F
16 сентября 2008 в 14:39
2933

Функция 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)
jfs
16 сентября 2008 в 15:03
45

Использование ключа не только чище, но и более эффективно.

Mario F
13 октября 2009 в 07:14
5

Самый быстрый способ - добавить оператор newlist.reverse (). В противном случае вы можете определить сравнение, например, cmp = lambda x, y: - cmp (x ['name'], y ['name']).

Philluminati
20 ноября 2009 в 15:16
3

если значением сортировки является число, вы можете сказать: лямбда k: (k ['age'] * -1), чтобы получить обратную сортировку

radicand
11 июля 2012 в 23:14
3

Это также относится к списку кортежей, если вы используете itemgetter(i), где i - это индекс элемента кортежа для сортировки.

Bakuriu
7 сентября 2012 в 17:59
48

itemgetter принимает более одного аргумента: itemgetter(1,2,3) - это функция, возвращающая кортеж, например obj[1], obj[2], obj[3], поэтому вы можете использовать ее для выполнения сложных сортировок.

webenformasyon
15 октября 2019 в 09:13
0

в случае строк юникода import locale / locale.setlocale(locale.LC_ALL, "") / newlist = sorted(list_to_be_sorted, key=lambda k: locale.strxfrm(k['name'])) работает на основе coderhelper.com/questions/1097908/…

sattva_venu
28 января 2021 в 12:53
0

что, если мы не знаем значение ключа?

avatar
Tms91
17 сентября 2021 в 09:20
0

Как указано @Claudiu @monojohnny в разделе комментариев этого ответа,
дано:

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

для сортировки списка словарей по ключу 'age', 'name'
(как в операторе SQL ORDER BY age, name), вы можете использовать:

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

или аналогично

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

print(newlist)

[{'имя': 'Барт', 'возраст': 10},
{'имя': 'Милхаус', 'возраст': 10},
{'имя': 'Гомер', 'возраст': 39}]

avatar
swac
14 августа 2020 в 21:01
6

Если производительность вызывает беспокойство, я бы использовал 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

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

avatar
Bejür
14 августа 2020 в 21:00
8

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

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

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 мкс на цикл

avatar
Shank_Transformer
14 августа 2020 в 20:57
9

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

Отметьте этот.

avatar
vvladymyrov
14 августа 2020 в 20:53
13

Вот альтернативное общее решение - сортирует элементы словаря по ключам и значениям.

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

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)
Peter Mortensen
14 августа 2020 в 20:54
0

Что вы имеете в виду под "сортирует элементы словаря по ключам и значениям" ? Как происходит сортировка? В чем заключаются ценности?

avatar
kiriloff
14 августа 2020 в 20:48
23

Использование преобразования Шварца из 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. идиома, используемая для повышения эффективности сортировки списка элементов. Этот идиома подходит для сортировки на основе сравнения, когда порядок фактически на основе упорядочивания определенного свойства (ключа) элементов, где вычисление этого свойства является интенсивной операцией, которая следует выполнять минимальное количество раз. Шварцианский Преобразование примечательно тем, что в нем не используются именованные временные массивы.

Antti Haapala
15 февраля 2015 в 20:11
10

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

avatar
Dologan
14 августа 2020 в 20:43
61

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

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

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

njzk2
29 мая 2013 в 13:41
3

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

Permafacture
23 августа 2013 в 21:05
1

Комментарий njzk2 не сразу был мне понятен, поэтому я обнаружил следующее. Вы можете просто отсортировать дважды, как предлагает njzk2, или передать несколько аргументов в operator.itemgetter в верхнем ответе. Ссылка: coderhelper.com/questions/5212870/…

Winston Ewert
15 декабря 2013 в 04:55
17

Нет необходимости преобразовывать в строку. Просто верните кортеж в качестве ключа.

wouter bolsterlee
24 апреля 2015 в 13:59
0

Многократная сортировка - это простейшее универсальное решение без хаков: coderhelper.com/a/29849371/1805397

avatar
abby sobh
22 июля 2020 в 13:50
13

Использование пакета 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 тыс.) Списков 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
clp2
7 ноября 2016 в 04:05
4

Я запустил ваш код и обнаружил ошибку в аргументах timeit.Timer для Large Method Pandas: вы указываете "setup_small" вместо "setup_large". Изменение этого аргумента привело к тому, что программа запустилась без завершения, и я остановил ее более чем через 5 минут. Когда я запустил его с "timeit (1)", Large Method Pandas закончил за 7,3 секунды, что намного хуже, чем LC или LC2.

abby sobh
8 ноября 2016 в 22:58
0

Вы совершенно правы, это была моя оплошность. Больше не рекомендую для больших случаев! Я отредактировал ответ, чтобы просто допустить это как возможность, вариант использования все еще обсуждается.

avatar
uingtea
22 июля 2020 в 13:49
16

Иногда нам нужно использовать 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}]
Peter Mortensen
14 августа 2020 в 20:52
0

Почему нам нужно использовать lower () в этом случае?

avatar
19 декабря 2017 в 17:31
9

Если вам не нужен исходный 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}]
avatar
17 марта 2017 в 10:29
36
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']) 
avatar
vemury
16 сентября 2008 в 15:18
190
import operator

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

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

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

list_of_dicts.sort(key=operator.itemgetter('age'))
monojohnny
17 февраля 2010 в 13:10
11

Все равно совместить имя и возраст? (как в SQL ORDER BY по имени, возрасту?)

Claudiu
4 сентября 2013 в 22:21
37

@monojohnny: да, просто пусть ключ возвращает кортеж, key=lambda k: (k['name'], k['age']). (или key=itemgetter('name', 'age')). cmp кортежа будет сравнивать каждый элемент по очереди. это чертовски здорово.

TTT
21 февраля 2014 в 15:21
1

В документации (docs.python.org/2/tutorial/datastructures.html) необязательный аргумент key для list.sort() не описан. Есть идеи, где это найти?

Kevin
19 февраля 2015 в 14:56
2

@TTT: См. Документацию библиотеки для list и других.

avatar
Owen
16 сентября 2008 в 14:52
23

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

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

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'))
avatar
efotinis
16 сентября 2008 в 14:43
34
import operator
a_list_of_dicts.sort(key=operator.itemgetter('name'))

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

avatar
pjz
16 сентября 2008 в 14:39
84
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'])

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

Sam
1 декабря 2020 в 14:51
0

Что можно сделать, если ключ неизвестен и продолжает меняться? Я имею в виду список dicts с одним ключом и значением, но ключ и значение не могут быть определены, поскольку они продолжают меняться.

pjz
30 декабря 2020 в 01:02
0

Мне нужен еще один пример. Попробуйте отправить возможное решение на stackexchange codereview и спросите, есть ли способ лучше.

pjz
10 марта 2021 в 06:38
0

@Sam, если вы хотите отсортировать по значению единственного ключа в dict, даже если вы не знаете ключ, вы можете сделать key=lambda k: list(k.values())[0]

avatar
Bartosz Radaczyński
16 сентября 2008 в 14:36
25

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

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

Это будет отсортировано следующим образом:

sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))
avatar
Matej
16 сентября 2008 в 14:31
20

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

Peter Mortensen
14 августа 2020 в 20:51
1

Это слишком сильно зависит от ссылки. Вы можете дать более полный ответ?

Matej
17 августа 2020 в 04:25
0

Надлежащие ответы уже предоставлены и другими участниками. Не стесняйтесь оставить ссылку или удалить ответ.