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

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

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


当前回答

从Python 3.6开始,dict对象现在按插入顺序排序。这是Python 3.7规范中的正式版本。

>>> words = {"python": 2, "blah": 4, "alice": 3}
>>> dict(sorted(words.items(), key=lambda x: x[1]))
{'python': 2, 'alice': 3, 'blah': 4}

在此之前,您必须使用OrderedDict。

Python 3.7文档说明:

在3.7版中更改:字典顺序保证插入顺序该行为是CPython在3.6中的实现细节。

其他回答

正如Dilettant所指出的,Python 3.6现在将保持秩序!我想我应该分享我编写的一个函数,它简化了可迭代(元组、列表、dict)的排序。在后一种情况下,可以对键或值进行排序,并且可以考虑数值比较。仅适用于>=3.6!

当您尝试在包含字符串和int的可迭代对象上使用sorted时,sorted()将失败。当然,您可以使用str()强制字符串比较。然而,在某些情况下,您希望进行实际的数值比较,其中12小于20(字符串比较中不是这种情况)。所以我提出了以下建议。当您需要显式数字比较时,可以使用标志num_as_num,它将尝试通过将所有值转换为浮点数来执行显式数字排序。如果成功,它将进行数字排序,否则将诉诸字符串比较。

欢迎提出改进意见。

def sort_iterable(iterable, sort_on=None, reverse=False, num_as_num=False):
    def _sort(i):
      # sort by 0 = keys, 1 values, None for lists and tuples
      try:
        if num_as_num:
          if i is None:
            _sorted = sorted(iterable, key=lambda v: float(v), reverse=reverse)
          else:
            _sorted = dict(sorted(iterable.items(), key=lambda v: float(v[i]), reverse=reverse))
        else:
          raise TypeError
      except (TypeError, ValueError):
        if i is None:
          _sorted = sorted(iterable, key=lambda v: str(v), reverse=reverse)
        else:
          _sorted = dict(sorted(iterable.items(), key=lambda v: str(v[i]), reverse=reverse))
      
      return _sorted
      
    if isinstance(iterable, list):
      sorted_list = _sort(None)
      return sorted_list
    elif isinstance(iterable, tuple):
      sorted_list = tuple(_sort(None))
      return sorted_list
    elif isinstance(iterable, dict):
      if sort_on == 'keys':
        sorted_dict = _sort(0)
        return sorted_dict
      elif sort_on == 'values':
        sorted_dict = _sort(1)
        return sorted_dict
      elif sort_on is not None:
        raise ValueError(f"Unexpected value {sort_on} for sort_on. When sorting a dict, use key or values")
    else:
      raise TypeError(f"Unexpected type {type(iterable)} for iterable. Expected a list, tuple, or dict")

您还可以使用可以传递给参数键的自定义函数。

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)

尝试以下方法。让我们用以下数据定义一个名为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

您还可以创建“反向索引”

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]

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)