Статьи

Python 201: как отсортировать словарь по значению

На днях меня спросили, есть ли способ отсортировать словарь по значению. Если вы регулярно используете Python, то знаете, что словарная структура данных по определению является несортированным типом отображения. Некоторые будут определять dict как хеш-таблицу . Несмотря на это, мне нужен был способ сортировки вложенного словаря (то есть словаря словарей) по значению во вложенных словарях, чтобы я мог перебирать ключи в указанном порядке. Мы проведем некоторое время, глядя на реализацию, которую я нашел.

После поисков идей я наткнулся на ответ на StackOverflow, который сделал большую часть того, что я хотел. Мне пришлось немного изменить его, чтобы он сортировался, используя вложенные значения словаря, но это было на удивление легко. Прежде чем мы перейдем к ответу, мы должны быстро взглянуть на структуру данных. Вот вариант зверя за вычетом закрытых частей, которые были удалены для вашей безопасности:

mydict = {'0d6f4012-16b4-4192-a854-fe9447b3f5cb': 
          {'CLAIMID': '123456789',
           'CLAIMDATE': '20120508', 
           'AMOUNT': '365.64', 'EXPDATE': '20120831'}, 
          'fe614868-d0c0-4c62-ae02-7737dea82dba': 
          {'CLAIMID': '45689654', 
           'CLAIMDATE': '20120508', 
           'AMOUNT': '185.55', 'EXPDATE': '20120831'}, 
          'ca1aa579-a9e7-4ade-80a3-0de8af4bcb21': 
          {'CLAIMID': '98754651',
           'CLAIMDATE': '20120508', 
           'AMOUNT': '93.00', 'EXPDATE': '20120831'},
          'ccb8641f-c1bd-45be-8f5e-e39b3be2e0e3': 
          {'CLAIMID': '789464321',
           'CLAIMDATE': '20120508', 'AMOUNT': '0.00',
           'EXPDATE': ''}, 
          'e1c445c2-5148-4a08-9b7e-ff5ed51c43ed': 
          {'CLAIMID': '897987945', 
           'CLAIMDATE': '20120508', 
           'AMOUNT': '62.66', 'EXPDATE': '20120831'}, 
          '77ad6dd4-5704-4060-9c38-6a93721ef98e': 
          {'CLAIMID': '23212315',
           'CLAIMDATE': '20120508', 
           'AMOUNT': '41.05', 'EXPDATE': '20120831'}
          }

Теперь мы знаем, с чем имеем дело. Давайте кратко рассмотрим слегка измененный ответ, который я придумал:

sorted_keys = sorted(mydict.keys(), key=lambda y: (mydict[y]['CLAIMID']))

 Это довольно изящная строчка, но я думаю, что это немного сбивает с толку. Вот мое понимание того, как это работает. Отсортированная функция сортирует список (ключей в Словаре в) на основе ключа , который в данном случае является анонимной функцией (лямбда). Анонимной функции передается словарь плюс один из внешних ключей и внутренний ключ, по которому мы хотим отсортировать, который в данном случае является «CLAIMID». После сортировки возвращается новый список. Лично я нахожу лямбды немного запутанными, поэтому я обычно провожу немного времени, разбивая их в именованную функцию, просто чтобы понять их немного лучше. Итак, без лишних слов, вот функциональная версия того же скрипта:

#----------------------------------------------------------------------
def func(key):
    """"""
    return mydict[key]['CLAIMID']
 
sorted_keys = sorted(mydict.keys(), key=func)
 
for key in sorted_keys:
    print mydict[key]['CLAIMID']

 И просто для интереса, давайте напишем скрипт, который может сортировать вложенный словарь по ЛЮБОМУ из ключей внутри него.

mydict = {'0d6f4012-16b4-4192-a854-fe9447b3f5cb': 
          {'CLAIMID': '123456789',
           'CLAIMDATE': '20120508', 
           'AMOUNT': '365.64', 'EXPDATE': '20120831'}, 
          'fe614868-d0c0-4c62-ae02-7737dea82dba': 
          {'CLAIMID': '45689654', 
           'CLAIMDATE': '20120508', 
           'AMOUNT': '185.55', 'EXPDATE': '20120831'}, 
          'ca1aa579-a9e7-4ade-80a3-0de8af4bcb21': 
          {'CLAIMID': '98754651',
           'CLAIMDATE': '20120508', 
           'AMOUNT': '93.00', 'EXPDATE': '20120831'},
          'ccb8641f-c1bd-45be-8f5e-e39b3be2e0e3': 
          {'CLAIMID': '789464321',
           'CLAIMDATE': '20120508', 'AMOUNT': '0.00',
           'EXPDATE': ''}, 
          'e1c445c2-5148-4a08-9b7e-ff5ed51c43ed': 
          {'CLAIMID': '897987945', 
           'CLAIMDATE': '20120508', 
           'AMOUNT': '62.66', 'EXPDATE': '20120831'}, 
          '77ad6dd4-5704-4060-9c38-6a93721ef98e': 
          {'CLAIMID': '23212315',
           'CLAIMDATE': '20120508', 
           'AMOUNT': '41.05', 'EXPDATE': '20120831'}
          }
 
outer_keys = mydict.keys()
print "outer keys:"
for outer_key in outer_keys:
    print outer_key
 
print "*" * 40
inner_keys = mydict[outer_key].keys()
 
for key in inner_keys:
    sorted_keys = sorted(mydict.keys(), key=lambda y: (mydict[y][key]))
    print "sorted by: " + key
    print sorted_keys
    for outer_key in sorted_keys:
        print mydict[outer_key][key]
    print "*" * 40
    print

 Этот код работает, но он не дает ожидаемых результатов. Попробуйте запустить это, и вы заметите, что вывод довольно странный. Сортировка выполняется по строкам, поэтому все значения, похожие на числа, сортируются как строки. К сожалению! Большинство людей хотели бы, чтобы числа сортировались как числа, поэтому нам нужно быстро преобразовать числовые значения в целые числа или числа с плавающей точкой. Вот окончательная версия кода (да, это немного небрежно):

mydict = {'0d6f4012-16b4-4192-a854-fe9447b3f5cb': 
          {'CLAIMID': '123456789',
           'CLAIMDATE': '20120508', 
           'AMOUNT': '365.64', 'EXPDATE': '20120831'}, 
          'fe614868-d0c0-4c62-ae02-7737dea82dba': 
          {'CLAIMID': '45689654', 
           'CLAIMDATE': '20120508', 
           'AMOUNT': '185.55', 'EXPDATE': '20120831'}, 
          'ca1aa579-a9e7-4ade-80a3-0de8af4bcb21': 
          {'CLAIMID': '98754651',
           'CLAIMDATE': '20120508', 
           'AMOUNT': '93.00', 'EXPDATE': '20120831'},
          'ccb8641f-c1bd-45be-8f5e-e39b3be2e0e3': 
          {'CLAIMID': '789464321',
           'CLAIMDATE': '20120508', 'AMOUNT': '0.00',
           'EXPDATE': ''}, 
          'e1c445c2-5148-4a08-9b7e-ff5ed51c43ed': 
          {'CLAIMID': '897987945', 
           'CLAIMDATE': '20120508', 
           'AMOUNT': '62.66', 'EXPDATE': '20120831'}, 
          '77ad6dd4-5704-4060-9c38-6a93721ef98e': 
          {'CLAIMID': '23212315',
           'CLAIMDATE': '20120508', 
           'AMOUNT': '41.05', 'EXPDATE': '20120831'}
          }
 
outer_keys = mydict.keys()
print "outer keys:"
for outer_key in outer_keys:
    print outer_key
 
print "*" * 40
inner_keys = mydict[outer_key].keys()
 
for outer_key in outer_keys:
    for inner_key in inner_keys:
        if mydict[outer_key][inner_key] == "":
            continue
        try:
            mydict[outer_key][inner_key] = int(mydict[outer_key][inner_key])
        except ValueError:
            mydict[outer_key][inner_key] = float(mydict[outer_key][inner_key])
 
for key in inner_keys:
    sorted_keys = sorted(mydict.keys(), key=lambda y: (mydict[y][key]))
    print "sorted by: " + key
    print sorted_keys
    for outer_key in sorted_keys:
        print mydict[outer_key][key]
    print "*" * 40
    print

Так что теперь мы отсортировали его так, чтобы это было более естественно для человеческого восприятия. Теперь есть еще один способ, которым мы могли бы сделать это, и сортировать данные так, как мы хотим, ДО того, как мы поместим их в нашу структуру данных. Однако это будет работать только в том случае, если мы используем OrderedDict из модуля коллекций, начиная с Python 2.7. Вы можете прочитать об этом в официальной документации .

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