【问题标题】:Sorting a nested OrderedDict by key, recursively递归地按键对嵌套的 OrderedDict 进行排序
【发布时间】:2014-05-08 10:21:01
【问题描述】:

假设orig 是一个OrderedDict,其中包含普通的字符串:字符串键值对,但有时该值可能是另一个嵌套的OrderedDict

我想按字母顺序(升序)按键对orig 进行排序,然后递归地进行

规则:

  • 假设键字符串是不可预测的
  • 假设嵌套可以无限进行,例如1-50 级都有字符串、OrderedDicts 等作为值。

sorted 算法方面需要帮助:

import string
from random import choice


orig = OrderedDict((
    ('a', choice(string.digits)),
    ('b', choice(string.digits)),
    ('c', choice(string.digits)),
    ('special', OrderedDict((
        ('a', choice(string.digits)),
        ('b', choice(string.digits)),
        ('c', choice(string.digits)),
    )))
))

sorted_copy = OrderedDict(sorted(orig.iteritems(), ...))

self.assertEqual(orig, sorted_copy)

【问题讨论】:

  • 只有一层深度吗?或者special对应的OrderedDict里面可以有另一个OrderedDict
  • @SukritKalra 我在问题中添加了一个Rules 部分,希望能找到一个无需专门了解 OrderedDict 结构即可重新利用的解决方案。
  • @StevenRumbalski 需要它来安全地创建包含许多不同嵌套数据类型的字典的校验和。 dict key order 之类的小东西正在改变校验和,即使 dict 的内容在技术上没有改变。在使用hashlib.md5(str(my_dict).hexdigest() 快速计算字典的散列之前对这些键进行排序让我可以安全地比较未来的版本。 (真正的原因是 MongoDB 不保证每个对象的顺序:docs.mongodb.org/manual/core/document/#document-limitations)。 TLDR:...长篇大论

标签: python sorting recursion ordereddictionary


【解决方案1】:

@pelson's answer@cjbarth's answer 的组合,带有 keyreverse 参数:

def deep_sorted(obj, *, key=None, reverse=False):
    if isinstance(obj, dict):
        return {k: deep_sorted(v, key=key, reverse=reverse) for k, v in sorted(obj.items(), key=key, reverse=reverse)}
    if isinstance(obj, list):
        return [deep_sorted(v, key=key, reverse=reverse) for i, v in sorted(enumerate(obj), key=key, reverse=reverse)]
    return obj

【讨论】:

    【解决方案2】:

    我在获取稳定对象时遇到了一个非常相似的问题,因此我可以获得稳定的哈希,除了我的对象混合了列表和字典,所以我必须对所有字典进行排序,首先是深度,然后再排序列表。这扩展了@acushneranswer

    def deep_sort(obj):
        if isinstance(obj, dict):
            obj = OrderedDict(sorted(obj.items()))
            for k, v in obj.items():
                if isinstance(v, dict) or isinstance(v, list):
                    obj[k] = deep_sort(v)
    
        if isinstance(obj, list):
            for i, v in enumerate(obj):
                if isinstance(v, dict) or isinstance(v, list):
                    obj[i] = deep_sort(v)
            obj = sorted(obj, key=lambda x: json.dumps(x))
    
        return obj
    

    顺便说一句,如果您发现自己的对象中有需要排序的类,您可以jsonpickle.dumps() 他们,然后json.loads() 他们,然后deep_sort() 他们。如果这很重要,那么您总是可以json.dumps()jsonpickle.loads() 回到您开始的地方,除了排序(好吧,仅在 Python 3.6+ 中排序)。但是对于稳定哈希的情况,这不是必需的。

    【讨论】:

      【解决方案3】:

      编辑:对于 python 3.6+,@pelson 的答案更好

      类似:

      def sortOD(od):
          res = OrderedDict()
          for k, v in sorted(od.items()):
              if isinstance(v, dict):
                  res[k] = sortOD(v)
              else:
                  res[k] = v
          return res
      

      【讨论】:

        【解决方案4】:

        @acushner 的解决方案现在可以在 python3.6+ 中简化,因为字典现在保留了它们的插入顺序。

        鉴于我们现在可以使用标准字典,代码现在看起来像:

        def order_dict(dictionary):
            result = {}
            for k, v in sorted(dictionary.items()):
                if isinstance(v, dict):
                    result[k] = order_dict(v)
                else:
                    result[k] = v
            return result
        

        因为我们可以使用标准字典,我们也可以使用标准字典推导,所以代码归结为:

        def order_dict(dictionary):
            return {k: order_dict(v) if isinstance(v, dict) else v
                    for k, v in sorted(dictionary.items())}
        

        另请参阅https://mail.python.org/pipermail/python-dev/2016-September/146327.html,了解有关 python 的有序字典实现的详细信息。此外,声明这将是 python 3.7 的语言功能:https://mail.python.org/pipermail/python-dev/2017-December/151283.html

        【讨论】:

          【解决方案5】:

          与@acushner 的解决方案非常相似,但基于类:

          from collections import OrderedDict
          
          
          class SortedDict(OrderedDict):
          
              def __init__(self, **kwargs):
                  super(SortedDict, self).__init__()
          
                  for key, value in sorted(kwargs.items()):
                      if isinstance(value, dict):
                          self[key] = SortedDict(**value)
                      else:
                          self[key] = value
          

          用法:

          sorted_dict = SortedDict(**unsorted_dict)
          

          【讨论】:

          • 不错。如果在插入额外值或调用 my_sorted_dict.sort() 时仍保持排序,则获得额外积分
          • 这可以通过覆盖您选择的迭代器(或全部)来实现: def items(self): for key, value in sorted(self.keys()): yield key, value字典中的项目将在读取而不是插入时排序。
          猜你喜欢
          • 2017-12-05
          • 2015-04-17
          • 1970-01-01
          • 2011-12-23
          • 2014-09-05
          • 2018-07-03
          • 2019-08-12
          • 2020-01-22
          • 1970-01-01
          相关资源
          最近更新 更多