Как отсортировать словарь, имеющий ключи как строку чисел в Python

У меня есть словарь:

a = {'100':12,'6':5,'88':3,'test':34, '67':7,'1':64 }

Я хочу отсортировать этот словарь по ключу, чтобы он выглядел так:

a = {'1':64,'6':5,'67':7,'88':3, '100':12,'test':34 }

6 ответов

Решение

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

Я хотел бы добавить одну вещь: если вы просто хотите просмотреть элементы словаря в отсортированном порядке, это просто:

for k in sorted(a):
    print k, a[k] # or whatever.

Если вы хотели бы иметь понимание списка (по Алекс):

sortedlist = [(k, a[k]) for k in sorted(a)]

Я хотел бы отметить, что Алекс использует key=int не будет работать с вашим примером, потому что один из ваших ключей 'test', Если вы действительно хотите, чтобы числа сортировались перед нечисловыми значениями, вам придется передать cmp функция:

def _compare_keys(x, y):
    try:
        x = int(x)
    except ValueError:
        xint = False
    else:
        xint = True
    try:
        y = int(y)
    except ValueError:
        if xint:
            return -1
        return cmp(x.lower(), y.lower())
        # or cmp(x, y) if you want case sensitivity.
    else:
        if xint:
            return cmp(x, y)
        return 1

for k in sorted(a, cmp=_compare_keys):
    print k, a[k] # or whatever.

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

# Won't work for integers with more than this many digits, or negative integers.
MAX_DIGITS = 10
def _keyify(x):
    try:
        xi = int(x)
    except ValueError:
        return 'S{0}'.format(x)
    else:
        return 'I{0:0{1}}'.format(xi, MAX_DIGITS)

for k in sorted(a, key=_keyify):
    print k, a[k] # or whatever.

Это было бы намного быстрее, чем при использовании cmp функция.

Вы не можете сортировать dict в Python как dict тип по своей природе неупорядочен. Что вы можете сделать, это отсортировать элементы, прежде чем использовать их с помощью sorted() встроенная функция. Вам также понадобится вспомогательная функция, чтобы различать ваши числовые и строковые ключи:

def get_key(key):
    try:
        return int(key)
    except ValueError:
        return key
a = {'100':12,'6':5,'88':3,'test':34, '67':7,'1':64 }
print sorted(a.items(), key=lambda t: get_key(t[0]))

Однако в Python 3.1 (и 2.7) collections модуль содержит collections.OrderedDict Тип, который может быть использован для достижения желаемого эффекта, как показано ниже:

def get_key(key):
    try:
        return int(key)
    except ValueError:
        return key
a = {'100':12,'6':5,'88':3,'test':34, '67':7,'1':64 }
b = collections.OrderedDict(sorted(a.items(), key=lambda t: get_key(t[0])))
print(b)

9 лет назад я выложил рецепт, который начинается

Словари не могут быть отсортированы - отображение не имеет порядка!

и показывает, как получить отсортированные списки из ключей и значений dict.

С сегодняшним Python и вашими явно выраженными плюсами я бы предложил:

import sys

def asint(s):
    try: return int(s), ''
    except ValueError: return sys.maxint, s

sortedlist = [(k, a[k]) for k in sorted(a, key=asint)]

key=asint это то, что говорит sorted обрабатывать эти строковые ключи как целые числа для целей сортировки, например, '2' сортирует между '1' а также '12'вместо того, чтобы следовать за ними обоими - это то, что вам, по-видимому, требуется, а также иметь все нецифровые ключи для сортировки после всех многозначных. Если вам также нужно иметь дело с цепочками из всех цифр, которые выражают целые числа больше sys.maxint, это немного сложнее, но все же выполнимо:

class Infinity(object):
    def __cmp__(self, other): return 0 if self is other else 1
infinite = Infinity()
def asint(s):
    try: return int(s), ''
    except ValueError: return infinite, s

В целом, вы можете получить лучшие ответы быстрее, если будете более точно указывать свои точные требования;-).

Словари неупорядочены. Вы не можете сортировать, как вы показываете, потому что результат a это диктат, а у диктов нет порядка.

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

>>> def my_key(dict_key):
...     try:
...         return int(dict_key)
...     except ValueError:
...         return dict_key
...
>>> sorted(a, key=my_key)
['1', '6', '67', '88', '100', 'test']

Это опирается на глупое поведение Python, что экземпляры str всегда больше, чем случаи int, (Поведение исправлено в Python 3.) В оптимальной схеме ключи вашего dict будут такими вещами, которые вы могли бы разумно сравнить, и вы бы не смешивали строки, представляющие числа, со строками, представляющими слова.

Если вы хотите хранить ключи в всегда отсортированном порядке, вы можете использовать bisect модуль или реализовать отображение, которое опирается на древовидную структуру данных. bisect модуль не принимает key аргумент, как сортировка, потому что это было бы потенциально неэффективно; вы бы использовали шаблон decorate – use – undecorate, если бы выбрали bisectсохраняя отсортированный список, который зависит от результата ключевой функции.

Если вы установите мой пакет blist, он включает в себя sorteddict тип. Тогда вы могли бы просто:

from blist import sorteddict

def my_key(dict_key):
       try:
              return int(dict_key)
       except ValueError:
              return dict_key

a = {'100':12,'6':5,'88':3,'test':34, '67':7,'1':64 }
print sorteddict(my_key, **a).keys()

Выход:

['1', '6', '67', '88', '100', 'test']

вы также можете сделать следующее:

      sorted_keys = sorted(list(a.keys()), key = lambda x: (len(x),x))
sorted_dict = {k:a[k] for k in sorted_keys}

Самая важная часть - это key = lambda x: (len(x),x)который я взял отсюда

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