У меня есть список словарей, и я хочу, чтобы каждый элемент был отсортирован по определенному значению.
Обратите внимание на список:
[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
При сортировке по name он должен выглядеть следующим образом:
[{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]






Вы должны реализовать свою собственную функцию сравнения, которая будет сравнивать словари по значениям ключей имен. См. Сортировка мини-КАК из PythonInfo Wiki
Это слишком сильно зависит от ссылки. Можете дать более полный ответ?
Надлежащие ответы уже предоставлены и другими участниками. Не стесняйтесь оставить ссылку или удалить ответ.
Я думаю, вы имели в виду:
[{'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 с одним ключом и значением, но ключ и значение не могут быть определены, поскольку они продолжают меняться.
Мне нужен еще один пример. Попробуйте отправить возможное решение на stackexchange codereview и спросите, есть ли способ лучше.
@Sam, если вы хотите отсортировать по значению единственного ключа в dict, даже если вы не знаете ключ, вы можете сделать key=lambda k: list(k.values())[0]
Это может выглядеть чище, если использовать ключ вместо 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)
Использование ключа не только чище, но и более эффективно.
Самый быстрый способ - добавить оператор newlist.reverse (). В противном случае вы можете определить сравнение, например, cmp = lambda x, y: - cmp (x ['name'], y ['name']).
если значением сортировки является число, вы можете сказать: лямбда k: (k ['age'] * -1), чтобы получить обратную сортировку
Это также относится к списку кортежей, если вы используете itemgetter(i), где i - это индекс элемента кортежа для сортировки.
itemgetter принимает более одного аргумента: itemgetter(1,2,3) - это функция, возвращающая кортеж, например obj[1], obj[2], obj[3], поэтому вы можете использовать ее для выполнения сложных сортировок.
в случае строк юникода import locale / locale.setlocale(locale.LC_ALL, "") / newlist = sorted(list_to_be_sorted, key=lambda k: locale.strxfrm(k['name'])) работает на основе stackoverflow.com/questions/1097908/…
что, если мы не знаем значение ключа?
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: да, только ключ возвращает кортеж, key=lambda k: (k['name'], k['age']). (или key=itemgetter('name', 'age')). cmp кортежа будет сравнивать каждый элемент по очереди. это чертовски здорово.
В документации (docs.python.org/2/tutorial/datastructures.html) необязательный аргумент key для list.sort() не описан. Есть идеи, где это найти?
@TTT: См. документация библиотеки для list и друзей.
Если вы хотите отсортировать список по нескольким ключам, вы можете сделать следующее:
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 не сразу был мне понятен, поэтому я обнаружил следующее. Вы можете просто отсортировать дважды, как предлагает njzk2, или передать несколько аргументов в operator.itemgetter в верхнем ответе. Ссылка: stackoverflow.com/questions/5212870/…
Нет необходимости преобразовывать в строку. Просто верните кортеж в качестве ключа.
Многократная сортировка - это простейшее универсальное решение без хаков: stackoverflow.com/a/29849371/1805397
Используя Преобразование Шварца из 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 лет.
Допустим, у меня есть словарь 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)
Что вы имеете в виду под "сортирует элементы словаря по ключам и значениям"? Как происходит сортировка? В чем заключаются ценности?
Использование пакета Панды - еще один метод, хотя его время выполнения в большом масштабе намного медленнее, чем более традиционные методы, предложенные другими:
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.
Вы совершенно правы, это была моя оплошность. Больше не рекомендую для больших случаев! Я отредактировал ответ, чтобы просто допустить это как возможность, вариант использования все еще обсуждается.
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}]
Если вам не нужен исходный 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
Оба метода сортируют список в одном и том же порядке (проверяется выполнением последнего оператора в блоке кода), но первый работает немного быстрее.
Читал ответ и смотрел 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.