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

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

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


当前回答

从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。

其他回答

使用namedtuple通常非常方便。例如,您有一个字典,其中“name”作为关键字,“score”作为值,您希望按“score“排序:

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)

现在,你可以得到的名字和分数,让我们说第二个最好的球员(索引=1)非常像这样:

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

您可以使用跳过字典,它是一个按值永久排序的字典。

>>> 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(),则将按值排序迭代。

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

如果值是数字,则还可以使用集合中的计数器。

from collections import Counter

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

>> [('python', 5), ('world', 3), ('hello', 1)]    
from django.utils.datastructures import SortedDict

def sortedDictByKey(self,data):
    """Sorted dictionary order by key"""
    sortedDict = SortedDict()
    if data:
        if isinstance(data, dict):
            sortedKey = sorted(data.keys())
            for k in sortedKey:
                sortedDict[k] = data[k]
    return sortedDict

另一个答案中提到的集合解决方案绝对是极好的,因为您在键和值之间保留了一种联系,这在字典中是极其重要的。

我不同意另一个答案中的第一选择,因为它会扔掉钥匙。

我使用了上面提到的解决方案(代码如下所示),并保留了对键和值的访问权,在我的情况下,排序是对值进行的,但重要的是对值排序后的键排序。

from collections import Counter

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


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