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

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

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

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

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

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

Читал ответ и смотрел 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') EDIT: Протестировано, и он работает, но я не знаю, как записать DESC и назвать ASC.

Claudiu 21.05.2020 10:13
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2 139
1
847 361
17
Перейти к ответу Данный вопрос помечен как решенный

Ответы 17

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

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

Peter Mortensen 14.08.2020 23:51

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

Matej 17.08.2020 07:25

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

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

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

sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))

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, но ваш пробег может отличаться.

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

Sam 01.12.2020 17:51

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

pjz 30.12.2020 04:02

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

pjz 10.03.2021 09:38
Ответ принят как подходящий

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

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

или, как предположили Дж. Ф. Себастьян и другие,

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

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

newlist = sorted(l, key=itemgetter('name'), reverse=True)

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

jfs 16.09.2008 19:03

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

Mario F 13.10.2009 11:14

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

Philluminati 20.11.2009 18:16

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

radicand 12.07.2012 03:14

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

Bakuriu 07.09.2012 21:59

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

webenformasyon 15.10.2019 12:13

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

sattva_venu 28.01.2021 15:53

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

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

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

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

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

import operator

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

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

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

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

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

monojohnny 17.02.2010 16:10

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

Claudiu 05.09.2013 02:21

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

TTT 21.02.2014 19:21

@TTT: См. документация библиотеки для list и друзей.

Kevin 19.02.2015 17:56

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

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.05.2013 17:41

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

Permafacture 24.08.2013 01:05

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

Winston Ewert 15.12.2013 08:55

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

wouter bolsterlee 24.04.2015 16:59

Используя Преобразование Шварца из 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'}]

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

In computer science, the Schwartzian transform is a Perl programming idiom used to improve the efficiency of sorting a list of items. This idiom is appropriate for comparison-based sorting when the ordering is actually based on the ordering of a certain property (the key) of the elements, where computing that property is an intensive operation that should be performed a minimal number of times. The Schwartzian Transform is notable in that it does not use named temporary arrays.

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

Antti Haapala 15.02.2015 23:11

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

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

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

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

Peter Mortensen 14.08.2020 23:54

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

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

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

clp2 07.11.2016 07:05

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

abby sobh 09.11.2016 01:58

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

Иногда нам нужно использовать 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}]
Почему do we need to use lower() in this case?
Peter Mortensen 14.08.2020 23:52

Если вам не нужен исходный 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}]

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

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

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 loops, best of 3: 0.736 µsec per loop

# 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 loops, best of 3: 0.438 µsec per loop

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

От https://wiki.python.org/moin/PythonSpeed:

Likewise, the builtin functions run faster than hand-built equivalents. For example, map(operator.add, v1, v2) is faster than 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

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

Другие вопросы по теме