如何按键对字典进行排序?

1489

如何根据字典的键进行排序?

示例输入:

{2:3, 1:89, 4:5, 3:0}

期望的输出:

{1:89, 2:3, 3:0, 4:5}

6
我的使用场景是我有一个CLI应用程序,它有一个简单的菜单,菜单选项作为字典键存在。我想按字母顺序显示这些键,以使用户更加方便使用。 - Randy
2
“字典未排序” - 并非普遍真实。例如,Java拥有TreeMap(https://docs.oracle.com/javase/8/docs/api/java/util/TreeMap.html),它的行为完全符合OP的要求。 - Nayuki
14
请注意,字典现在按照插入顺序排序(Python 3.6+)。以下部分答案指出了这一点。 - matiasg
4
请注意,在Python 3.6中,保持插入顺序的字典是CPython的一个实现细节。直到Python 3.7,字典的插入顺序保持才正式成为语言的一部分。 - robertspierre
排序后的字典 = dict(sorted(my_dict.items())) - undefined
33个回答

5

Python字典是无序的。通常,这不是问题,因为最常见的用例是查找。

实现你想要的最简单的方法是创建一个collections.OrderedDict并按排序顺序插入元素。

ordered_dict = collections.OrderedDict([(k, d[k]) for k in sorted(d.keys())])

如果您需要迭代,如其他人所建议的那样,最简单的方法是迭代排序后的键。示例-
按键排序打印值:
# create the dict
d = {k1:v1, k2:v2,...}
# iterate by keys in sorted order
for k in sorted(d.keys()):
    value = d[k]
    # do something with k, value like print
    print k, value

按键排序,获取值列表:

values = [d[k] for k in sorted(d.keys())]

2
使用 for k,value in sorted(d.items()): 更好:避免在循环中再次通过键访问字典。 - Jean-François Fabre

5

此函数将对任何字典进行递归排序,即如果字典中的任何值也是一个字典,则它也将按其键进行排序。 如果您在运行CPython 3.6或更高版本,则可以简单修改使用dict而不是OrderedDict。

from collections import OrderedDict

def sort_dict(d):
    items = [[k, v] for k, v in sorted(d.items(), key=lambda x: x[0])]
    for item in items:
        if isinstance(item[1], dict):
            item[1] = sort_dict(item[1])
    return OrderedDict(items)
    #return dict(items)

4

最简单的解决方案是获取一个键按排序顺序排列的字典列表,然后遍历该字典。例如:

a1 = {'a':1, 'b':13, 'd':4, 'c':2, 'e':30}
a1_sorted_keys = sorted(a1, key=a1.get, reverse=True)
for r in a1_sorted_keys:
    print r, a1[r]

以下是输出结果(按降序排列)
e 30
b 13
d 4
c 2
a 1

3

大家把事情复杂化了...其实很简单

from pprint import pprint
Dict={'B':1,'A':2,'C':3}
pprint(Dict)

输出结果为:
{'A':2,'B':1,'C':3}

点赞是因为我不知道pprint对字典进行排序以显示它们,但是楼主真正询问的是从未排序的字典“转换”为已排序的字典,即楼主似乎想要一些在内存中保持排序的东西,可能是为了某些需要排序键的算法。 - Captain Lepton
这个方法不允许链式赋值,因为pprint返回none。 >>> adict = {'B':1,'A':2,'C':3} >>> ppdict = pprint(adict) {'A': 2, 'B': 1, 'C': 3} >>> ppdict.type() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'NoneType' object has no attribute 'type' - user4322543

3

对于问题的表述方式,这里大多数答案都回答得很正确。

然而,考虑到如何真正应该做的事情,考虑到几十年的计算机科学,令我完全惊讶的是,实际上只有 一个答案(来自GrantJ用户)建议使用排序的关联容器(sortedcontainers),它根据键在插入点处对元素进行排序。

这将避免每次调用 sort(...)(至少为O(N*log(N)),其中N是元素数量)所带来的巨大性能影响(从逻辑上讲,对于所有建议使用sort(...)的解决方案都适用)。请注意,在添加/删除元素后,每次访问集合时都需要调用sort(...)以按排序方式访问。


2
dictionary = {1:[2],2:[],5:[4,5],4:[5],3:[1]}

temp=sorted(dictionary)
sorted_dict = dict([(k,dictionary[k]) for i,k in enumerate(temp)])

sorted_dict:
         {1: [2], 2: [], 3: [1], 4: [5], 5: [4, 5]}

2
from operator import itemgetter
# if you would like to play with multiple dictionaries then here you go:
# Three dictionaries that are composed of first name and last name.
user = [
    {'fname': 'Mo', 'lname': 'Mahjoub'},
    {'fname': 'Abdo', 'lname': 'Al-hebashi'},
    {'fname': 'Ali', 'lname': 'Muhammad'}
]
#  This loop will sort by the first and the last names.
# notice that in a dictionary order doesn't matter. So it could put the first name first or the last name first. 
for k in sorted (user, key=itemgetter ('fname', 'lname')):
    print (k)

# This one will sort by the first name only.
for x in sorted (user, key=itemgetter ('fname')):
    print (x)

1

或者使用 pandas

演示:

>>> d={'B':1,'A':2,'C':3}
>>> df=pd.DataFrame(d,index=[0]).sort_index(axis=1)
   A  B  C
0  2  1  3
>>> df.to_dict('int')[0]
{'A': 2, 'B': 1, 'C': 3}
>>> 

请参见:

此文档

pandas完整文档


1
在2.7中对这两种方法进行的时间比较表明它们几乎相同。
>>> setup_string = "a = sorted(dict({2:3, 1:89, 4:5, 3:0}).items())"
>>> timeit.timeit(stmt="[(k, val) for k, val in a]", setup=setup_string, number=10000)
0.003599141953657181

>>> setup_string = "from collections import OrderedDict\n"
>>> setup_string += "a = OrderedDict({1:89, 2:3, 3:0, 4:5})\n"
>>> setup_string += "b = a.items()"
>>> timeit.timeit(stmt="[(k, val) for k, val in b]", setup=setup_string, number=10000)
0.003581275490432745 

0
l = dict.keys()
l2 = l
l2.append(0)
l3 = []
for repeater in range(0, len(l)):
    smallnum = float("inf")
    for listitem in l2:
        if listitem < smallnum:
            smallnum = listitem
    l2.remove(smallnum)
    l3.append(smallnum)
l3.remove(0)
l = l3

for listitem in l:
    print(listitem)

3
还有其他14个答案。你能否简要解释一下你的代码,以及为什么它可能比其他解决方案更好? - FelixSFD
1
被踩 - 代码难以阅读,变量名简短且无意义,如l、l2、l3。 似乎是一种间接而低效的算法尝试,没有使用Python标准函数,并且在原帖中提供的小例子测试时无法正常工作。 - Captain Lepton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接