如何按值对字典排序?

我有一个从数据库的两个字段中读取的值的字典:字符串字段和数字字段。字符串字段是唯一的,因此这是字典的键。

我可以对键进行排序,但是如何根据值进行排序?

注意:我在这里阅读了堆栈溢出问题, 如何按字典值对字典列表进行排序?可能会更改我的代码以包含字典列表,但是由于我实际上并不需要字典列表,因此我想知道是否有更简单的解决方案来按升序或降序进行排序。

答案

Python 3.6+

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
{k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}

较旧的 Python

无法对字典进行排序,只能获得已排序字典的表示形式。字典本质上是无序的,但其他类型(例如列表和元组)不是。因此,您需要一个有序的数据类型来表示排序后的值,该值将是一个列表 - 可能是一个元组列表。

例如,

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

sorted_x将是一个元组列表,按每个元组中的第二个元素排序。 dict(sorted_x) == x

对于那些希望对键而不是值进行排序的人:

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

在 Python3 中,由于不允许拆包[1],我们可以使用

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=lambda kv: kv[1])

如果要将输出作为字典,则可以使用collections.OrderedDict

import collections

sorted_dict = collections.OrderedDict(sorted_x)

就像这样简单: sorted(dict1, key=dict1.get)

好吧,实际上可以执行 “按字典值排序”。最近,我不得不在 Code Golf(堆栈溢出问题Code golf:单词频率图表 )中进行此操作。简而言之,问题是这样的:给定一个文本,计算遇到每个单词的频率,并显示按频率递减排序的最重要单词列表。

如果您以单词为键构建字典,每个单词的出现次数为值,则简化为:

from collections import defaultdict
d = defaultdict(int)
for w in text.split():
  d[w] += 1

那么您可以获取单词列表,并按sorted(d, key=d.get)的使用频率sorted(d, key=d.get) - 使用单词出现的次数作为 sort key,排序遍历字典键。

for w in sorted(d, key=d.get, reverse=True):
  print w, d[w]

我正在写这个详细的说明,以说明人们通常所说的 “我可以很容易地按键对字典进行排序,但是如何按值进行排序” 的意思 - 我认为 OP 试图解决这一问题。解决方案是根据值对键列表进行排序,如上所示。

您可以使用:

sorted(d.items(), key=lambda x: x[1])

这将按照字典中每个条目的值(从最小到最大)对字典进行排序。

要将其降序排序,只需添加reverse=True

sorted(d.items(), key=lambda x: x[1], reverse=True)

输入:

d = {'one':1,'three':3,'five':5,'two':2,'four':4}
a = sorted(d.items(), key=lambda x: x[1])    
print(a)

输出:

[('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)]

字典无法排序,但是您可以从中建立排序列表。

字典值的排序列表:

sorted(d.values())

(键,值)对的列表,按值排序:

from operator import itemgetter
sorted(d.items(), key=itemgetter(1))

在最近的 Python 2.7 中,我们有了新的OrderedDict类型,该类型可以记住添加项目的顺序。

>>> d = {"third": 3, "first": 1, "fourth": 4, "second": 2}

>>> for k, v in d.items():
...     print "%s: %s" % (k, v)
...
second: 2
fourth: 4
third: 3
first: 1

>>> d
{'second': 2, 'fourth': 4, 'third': 3, 'first': 1}

要从原始字典中重新排序,请按以下值排序:

>>> from collections import OrderedDict
>>> d_sorted_by_value = OrderedDict(sorted(d.items(), key=lambda x: x[1]))

OrderedDict 的行为类似于普通字典:

>>> for k, v in d_sorted_by_value.items():
...     print "%s: %s" % (k, v)
...
first: 1
second: 2
third: 3
fourth: 4

>>> d_sorted_by_value
OrderedDict([('first': 1), ('second': 2), ('third': 3), ('fourth': 4)])

更新:2015 年 12 月 5 日使用 Python 3.5

尽管我发现接受的答案很有用,但令我感到惊讶的是,它没有被更新为从标准库集合模块中引用OrderedDict作为可行的现代替代方案,旨在解决这类问题。

from operator import itemgetter
from collections import OrderedDict

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = OrderedDict(sorted(x.items(), key=itemgetter(1)))
# OrderedDict([(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)])

官方的OrderedDict文档也提供了一个非常类似的示例,但对排序函数使用了 lambda:

# regular unsorted dictionary
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

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

Hank Gay 的答案几乎相同:

sorted([(value,key) for (key,value) in mydict.items()])

或根据 John Fouhy 的建议进行了稍微优化:

sorted((value,key) for (key,value) in mydict.items())

使用namedtuple通常很方便。例如,您有一个 “名称” 作为键,而 “分数” 作为值的字典,并且您想对 “分数” 进行排序:

import collections
Player = collections.namedtuple('Player', 'score name')
d = {'John':5, 'Alex':10, 'Richard': 7}

首先以最低分数排序:

worst = sorted(Player(v,k) for (k,v) in d.items())

首先以最高分排序:

best = sorted([Player(v,k) for (k,v) in d.items()], reverse=True)

现在您可以得到 Python 的第二好玩家(index = 1)的名称和得分,如下所示:

player = best[1]
player.name
    'Richard'
player.score
    7

Python 3.6 开始,将对内置字典进行排序

好消息,因此 OP 从数据库中检索到的映射对的原始用例(以唯一的字符串 ID 作为键,而数值作为值)到内置 Python v3.6 + dict 中,现在应该遵守插入顺序。

如果说从数据库查询中得到的两个列表表达式如下:

SELECT a_key, a_value FROM a_table ORDER BY a_value;

将存储在两个 Python 元组 k_seq 和 v_seq 中(按数字索引对齐,并且具有相同的长度),然后:

k_seq = ('foo', 'bar', 'baz')
v_seq = (0, 1, 42)
ordered_map = dict(zip(k_seq, v_seq))

允许以后输出为:

for k, v in ordered_map.items():
    print(k, v)

在这种情况下产生(对于新的 Python 3.6 + 内置字典!):

foo 0
bar 1
baz 42

以 v 的每个值相同的顺序排列。

当前在我的机器上的 Python 3.5 安装位置生成:

bar 1
foo 0
baz 42

细节:

正如 Raymond Hettinger 在 2012 年所提议的(参见 python-dev 上的邮件,主题为“更紧凑的字典,迭代速度更快” ),现在(2016 年),Victor Stinner 在给 python-dev 的邮件中宣布了主题为“ Python 3.6 dict 紧凑并获得私有版本;由于在 Python 3.6 中已解决 / 实现了问题 27350 “紧凑且有序的字典” ,因此关键字变得有序” ,我们现在可以使用内置的字典来维护插入顺序!!

希望这将导致第一步的薄层 OrderedDict 实现。正如 @ JimFasarakis-Hilliard 指出的那样,将来还会看到一些 OrderedDict 类型的用例。我认为整个 Python 社区都会仔细检查,是否经得起时间的考验,以及下一步将采取什么措施。

是时候重新考虑我们的编码习惯,以免错过以下稳定订单所带来的可能性:

  • 关键字参数和
  • (中级)字典存储

第一个是因为它在某些情况下简化了函数和方法的实现中的调度。

第二个原因是它鼓励在处理管道中更轻松地将dict用作中间存储。

Raymond Hettinger 从旧金山 Python Meetup Group 的演讲 2016-DEC-08 中提供了解释 “ Python 3.6 词典背后的技术文档。

也许相当一部分 Stack Overflow 高修饰度的问答页面会收到此信息的变体,并且许多高质量的答案也需要按版本进行更新。

警告购买者(另请参阅下面的更新 2017-12-15):

正如 @ajcr 正确指出的那样:“此新实现的顺序保留方面被认为是实现细节,因此不应依赖。” (摘自whatsnew36 )并不是很挑剔, 引文有点悲观了;-)。它继续显示为 “(将来可能会改变,但是希望在更改语言规范以强制所有当前和将来的 Python 实现保留顺序语义之前,先在几个版本中使用该语言的新 dict 实现;有助于保持与仍旧有效的随机迭代顺序的旧版语言(例如 Python 3.5)的向后兼容性。”

因此,就像在某些人类语言(例如德语)中一样,用法决定了语言的使用方式,现在遗嘱已在whatsnew36 中声明。

更新 2017-12-15:

发给 python-dev 列表邮件中 ,Guido van Rossum 声明:

做到这一点。裁定 “裁定保留插入顺序”。谢谢!

因此,dict 插入顺序的 3.6 CPython 版本的副作用现在已成为语言规范的一部分(并且不再仅仅是实现细节)。该邮件线程还浮出了collections.OrderedDict一些区别设计目标,正如 Raymond Hettinger 在讨论中所提醒的那样。

我有同样的问题,我这样解决了:

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

(回答 “无法对字典进行排序的人没有读过这个问题!实际上,“我可以对键进行排序,但是如何根据值进行排序?” 显然意味着他想要一个列表)键根据其值的值排序。)

请注意,顺序定义不正确(具有相同值的键在输出列表中将以任意顺序排列)。