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

我可以按关键字排序,但如何根据值排序?

注意:我在这里读过堆栈溢出问题。如何根据字典的值对字典列表进行排序?并且可能会更改我的代码,使其具有字典列表,但由于我确实不需要字典列表,我想知道是否有更简单的解决方案来按升序或降序排序。


当前回答

这是代码:

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}

其他回答

从Python 3.6开始,将订购内置dict

好消息是,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+内置dict!):

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期“紧凑和有序的dict”的修复/实现能够使用内置dict来维护插入顺序!!

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

是时候重新思考我们的编码习惯了,不要错过稳定排序带来的可能性:

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

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

第二,它鼓励更容易地将dict用作处理管道中的中间存储。

雷蒙德·赫廷格(Raymond Hettinger)在其旧金山Python Meetup Group演讲2016-DEC-08中善意地提供了解释“Python3.6词典背后的技术”的文档。

也许相当多的Stack Overflow高修饰问答页面会收到这些信息的变体,许多高质量的答案也需要每个版本的更新。

注意事项Emptor(但也可参见以下更新2017-12-15):

正如@ajcr正确地指出的:“这个新实现的顺序保持方面被认为是一个实现细节,不应该依赖它。”(来自whatsnew36)不是吹毛求疵,但引用有点悲观;-)。它继续为“(这在未来可能会发生变化,但在更改语言规范以强制所有当前和未来Python实现保持顺序的语义之前,希望在语言中使用这个新的dict实现几个版本;这也有助于保持与随机迭代顺序仍然有效的语言旧版本的向后兼容性,例如Python 3.5)。”

因此,正如在一些人类语言(如德语)中,用法塑造了语言,而现在已经宣布了意志。。。在whatsnew36中。

更新2017-12-15:

在发给python开发者列表的邮件中,Guido van Rossum声明:

这样做吧。“听写保持插入顺序”是规则。谢谢

因此,dict插入排序的3.6版CPython副作用现在已成为语言规范的一部分(不再只是实现细节)。正如Raymond Hettinger在讨论中提醒的那样,这条邮件线索还提出了一些与众不同的收藏设计目标。OrderedDict。

这是代码:

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}

当然,请记住,您需要使用OrderedDict,因为常规Python字典不能保持原始顺序。

from collections import OrderedDict
a = OrderedDict(sorted(originalDict.items(), key=lambda x: x[1]))

如果您没有Python 2.7或更高版本,最好的方法就是迭代生成器函数中的值。(这里有2.4和2.6的OrderedDict,但是

a) 我不知道它有多好

and

b) 当然,你必须下载并安装它。如果您没有管理权限,那么恐怕该选项无效。)


def gen(originalDict):
    for x, y in sorted(zip(originalDict.keys(), originalDict.values()), key=lambda z: z[1]):
        yield (x, y)
    #Yields as a tuple with (key, value). You can iterate with conditional clauses to get what you want. 

for bleh, meh in gen(myDict):
    if bleh == "foo":
        print(myDict[bleh])

您还可以打印出每个值

for bleh, meh in gen(myDict):
    print(bleh, meh)

如果不使用Python 3.0或更高版本,请记住在打印后删除括号

Python 3.7+或CPython 3.6

Python 3.7+中的字典保持插入顺序。CPython 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}

or

>>> dict(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中,由于不允许开箱,我们可以使用

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)

如果您的值是整数,并且使用Python 2.7或更高版本,则可以使用collections.Counter而不是dict。most_common方法将为您提供按值排序的所有项。