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

3414

我有一个从数据库中读取的键值对字典:一个字符串字段和一个数字字段。字符串字段是唯一的,所以它是字典的键。

我可以按照键排序,但是如何基于值排序呢?

注意:我已经在Stack Overflow上阅读了这里的问题:How do I sort a list of dictionaries by a value of the dictionary?,并且可能可以更改我的代码以使用字典列表,但由于我实际上不需要字典列表,我想知道是否有更简单的解决方案来升序或降序排序。


9
字典数据结构没有固有的顺序。虽然可以遍历它,但不能保证遍历的顺序是特定的。这是设计上的考虑,因此最好使用另一种数据结构来表示。 - Daishiman
135
"sorted()" 函数可以用于字典(并返回已排序的键列表),因此我认为他知道这一点。如果不了解其程序,就告诉别人他们使用了错误的数据结构是荒谬的。如果你需要90%的时间快速查找,则可能需要使用字典。 - bobpaul
这里清晰简洁地介绍了排序字典的三种输出方式(键、值、两者):https://dev59.com/nGQn5IYBdhLWcg3wg3aR - JStrahl
2
@Daishiman 基类可能没有排序,但是OrderedDict当然是有序的。 - Taylor D. Edmiston
1
在Python 3.6+中,字典保留插入顺序。当然,这并不意味着可以按值对它们进行排序,但另一方面,不能再说“字典数据结构没有固有的顺序”了。 - Konrad Kocik
34个回答

54

我遇到了相同的问题,我是这样解决的:

WantedOutput = sorted(MyDict, key=lambda x : MyDict[x]) 

回答“不可能对字典进行排序”的人没有读清楚问题!实际上,“我可以按键排序,但是如何根据值排序?”明显意味着他想要一个按照它们的值排序的键列表。

请注意顺序未被定义(具有相同值的键将以任意顺序出现在输出列表中)。


请注意,您正在迭代字典并通过其键获取值,因此从性能上讲,这不是最优的解决方案。 - Ron Klein
1
@Dejell:正如贡献者所说,他将问题解释为“我能否按值排序获取键列表”。我们不需要在结果中包含值,因为它们已经在字典中了。 - Max

52
如果值是数值类型,您也可以使用来自collectionsCounter
from collections import Counter

x = {'hello': 1, 'python': 5, 'world': 3}
c = Counter(x)
print(c.most_common())

>> [('python', 5), ('world', 3), ('hello', 1)]    

如果你的字典是这样的 >>> x={'hello':1,'python':5, 'world':300},那怎么办? - James Sapam
@yopy Counter({'hello':1, 'python':5, 'world':300}).most_common() 返回 [('world', 300), ('python', 5), ('hello', 1)]。实际上,这适用于任何可排序的值类型(尽管许多其他 Counter 操作需要将值与 int 进行比较)。 - lvc

42
在 Python 2.7 中,只需执行以下操作:
from collections import OrderedDict
# regular unsorted dictionary
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

# dictionary sorted by key
OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])

# dictionary sorted by value
OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

从这里复制粘贴: http://docs.python.org/dev/library/collections.html#ordereddict-examples-and-recipes

享受吧 ;-)


32

这是代码:

import operator
origin_list = [
    {"name": "foo", "rank": 0, "rofl": 20000},
    {"name": "Silly", "rank": 15, "rofl": 1000},
    {"name": "Baa", "rank": 300, "rofl": 20},
    {"name": "Zoo", "rank": 10, "rofl": 200},
    {"name": "Penguin", "rank": -1, "rofl": 10000}
]
print ">> Original >>"
for foo in origin_list:
    print foo

print "\n>> Rofl sort >>"
for foo in sorted(origin_list, key=operator.itemgetter("rofl")):
    print foo

print "\n>> Rank sort >>"
for foo in sorted(origin_list, key=operator.itemgetter("rank")):
    print foo

以下是结果:

原始的

{'name': 'foo', 'rank': 0, 'rofl': 20000}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Baa', 'rank': 300, 'rofl': 20}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Penguin', 'rank': -1, 'rofl': 10000}

Rofl

{'name': 'Baa', 'rank': 300, 'rofl': 20}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Penguin', 'rank': -1, 'rofl': 10000}
{'name': 'foo', 'rank': 0, 'rofl': 20000}

排名

{'name': 'Penguin', 'rank': -1, 'rofl': 10000}
{'name': 'foo', 'rank': 0, 'rofl': 20000}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Baa', 'rank': 300, 'rofl': 20}

32

尝试以下方法。我们定义一个名为 mydict 的字典,其中包含以下数据:

mydict = {'carl':40,
          'alan':2,
          'bob':1,
          'danny':3}

如果想要按键进行字典排序,可以进行以下操作:

for key in sorted(mydict.iterkeys()):
    print "%s: %s" % (key, mydict[key])

这应该返回以下输出:

alan: 2
bob: 1
carl: 40
danny: 3

另一方面,如果要按值对字典进行排序(正如问题所要求的那样),可以执行以下操作:

for key, value in sorted(mydict.iteritems(), key=lambda (k,v): (v,k)):
    print "%s: %s" % (key, value)

这个命令的结果(按值排序字典)应该返回以下内容:

bob: 1
alan: 2
danny: 3
carl: 40

太棒了!for key, value in sorted(mydict.iteritems(), key=lambda (k,v): v["score"]): 允许您按子键排序。 - Andomar
这在不支持元组拆包和字典不再具有iteritems()方法的后续Python版本中无法工作。 - lb_so

28

你可以创建一个“倒排索引”,也称为

from collections import defaultdict
inverse= defaultdict( list )
for k, v in originalDict.items():
    inverse[v].append( k )
现在你的倒转具有值;每个值都有一组适用的键。
for k in sorted(inverse):
    print k, inverse[k]

27

你可以使用 collections.Counter。注意,这适用于数值和非数值类型的值。

>>> x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
>>> from collections import Counter
>>> #To sort in reverse order
>>> Counter(x).most_common()
[(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)]
>>> #To sort in ascending order
>>> Counter(x).most_common()[::-1]
[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]
>>> #To get a dictionary sorted by values
>>> from collections import OrderedDict
>>> OrderedDict(Counter(x).most_common()[::-1])
OrderedDict([(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)])

7
这与Ivan Sas的回答有何不同? - Peter Mortensen

25
另一个答案中提到的集合解决方案非常出色,因为它保留了键和值之间的连接,在字典的情况下这非常重要。 我不同意另一个答案中提出的第一选择,因为它抛弃了键。 我使用上述提到的解决方案(代码如下所示),保留了对键和值的访问权限,在我的情况下排序是根据值进行的,但重要的是在对值排序后对键进行排序。
from collections import Counter

x = {'hello':1, 'python':5, 'world':3}
c=Counter(x)
print( c.most_common() )


>> [('python', 5), ('world', 3), ('hello', 1)]

21

你也可以使用一个自定义函数,该函数可以传递给参数 key

def dict_val(x):
    return x[1]

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=dict_val)

这是目前在Python 2.7中唯一有效的答案。 - rkochev
2
Python 2 的生命周期已于2020年结束。 - Peter Mortensen

19
你可以使用一个永久由值排序的字典,称为“skip dict”(跳跃字典)。这里提供了相关信息。
>>> data = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
>>> SkipDict(data)
{0: 0.0, 2: 1.0, 1: 2.0, 4: 3.0, 3: 4.0}

如果你使用keys()values()items(),那么你将按值的排序顺序进行迭代。

它是使用跳跃表数据结构实现的。


我们能改变排序的顺序吗?现在是升序,但我想要降序。 - user6304394
据我所知,您需要取反值以颠倒顺序。 - malthe

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