Как отсортировать словарь, имеющий ключи как строку чисел в Python - PullRequest
9 голосов
/ 30 марта 2010

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

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 }

Ответы [ 5 ]

12 голосов
/ 30 марта 2010

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

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

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.

6 голосов
/ 30 марта 2010

Вы не можете отсортировать 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)
5 голосов
/ 30 марта 2010

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

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

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

С сегодняшним 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

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

4 голосов
/ 30 марта 2010

Словари не упорядочены. Вы не можете сортировать, как показано, потому что результирующий 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, сохраняя отсортированный список, который зависит от результата ключевой функции.

2 голосов
/ 30 марта 2010

Если вы устанавливаете мой пакет 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']
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...