如何一次对字典或列表中的所有嵌套字典和列表进行排序?

Posted

技术标签:

【中文标题】如何一次对字典或列表中的所有嵌套字典和列表进行排序?【英文标题】:How to sort all the nested dictionaries and lists inside a dictionary or list at once? 【发布时间】:2021-11-01 04:56:57 【问题描述】:

我正试图为此目的开发最有效/最全面的功能:

对字典或列表中的每个嵌套字典或列表进行排序。

注意:我使用 collections.OrderedDict 是因为我想让它也适用于 3.7 之前的 python 版本,即那些不保留字典顺序的版本。

基于来自this thread 的递归函数,它只对嵌套字典进行排序,我正在尝试构建一个对应的递归函数,它只对嵌套列表进行排序,然后通过使用 if 循环来组合它们,以确定对象是否被排序的是字典或列表。

这是我开发的:

from collections import OrderedDict

def recursively_order_dict(d):
    ordered_dict = OrderedDict()
    for key in sorted(d.keys()):
        val = d[key]
        if isinstance(val, dict):
            val = recursively_order_dict(val)
        if isinstance(val, list):
            val = recursively_order_list(val)    
        ordered_dict[key] = val
    return ordered_dict

def recursively_order_list(l):
    ordered_list = []
    for element in sorted(l):
        if isinstance(element, list):
            element = recursively_order_list(element)
        if isinstance(element, dict):
            element = recursively_order_dict(element)
        ordered_list.append(element)
    return ordered_list 

def order_all_dicts_and_lists_in_iterable(iterable1):        
    if isinstance(iterable1, dict):
        ordered_iterable = recursively_order_dict(iterable1)            
    if isinstance(iterable1, list):
        ordered_iterable = recursively_order_list(iterable1)                   
    else:        
        print("%s\n is nor a list nor a dictionary.\nIts type is %s." % (iterable1, type(iterable1)) ) 
        return   
    return ordered_iterable    

它适用于许多示例,但不能通过处理字典dict_2

dict_2 =  
        "key9":"value9",
        "key5":"value5",
        "key3":
                "key3_1":"value3_1",
                "key3_5":"value3_5",
                "key3_2":[[],"value3_2_1",[] ],
                ,
        "key2":"value2",
        "key8":
                "key8_1":"value8_1",
                "key8_5":
                            "key8_5_4":["value8_5_b", "value8_5_a", "value8_5_c"],
                            "key8_5_2":[,"key8_5_2_4_2":"value8_5_2_4_2", "key8_5_2_4_1":"value8_5_2_4_1", "key8_5_2_4_5":"value8_5_2_4_5", "value8_5_2_1",],
                            ,
                "key8_2":"value8_2",
                ,
        "key1":"value1",
     

sorted_dict_2 = order_all_dicts_and_lists_in_iterable(dict_2)

并抛出此错误:

--------------------------------------------------------------------------- TypeError                                 Traceback (most recent call last) <ipython-input-12-9cbf4414127d> in <module>
----> 1 order_all_dicts_and_lists_in_iterable(dict_2)

<ipython-input-9-352b10801248> in order_all_dicts_and_lists_in_iterable(iterable1)
     26 
     27     if isinstance(iterable1, dict):
---> 28         ordered_iterable = recursively_order_dict(iterable1)
     29         if isinstance(iterable1, list):
     30             ordered_iterable = order_all_dicts_and_lists_in_iterable(ordered_iterable)

<ipython-input-9-352b10801248> in recursively_order_dict(d)
      6         val = d[key]
      7         if isinstance(val, dict):
----> 8             val = recursively_order_dict(val)
      9         if isinstance(val, list):
     10             val = recursively_order_list(val)

<ipython-input-9-352b10801248> in recursively_order_dict(d)
      8             val = recursively_order_dict(val)
      9         if isinstance(val, list):
---> 10             val = recursively_order_list(val)
     11         ordered_dict[key] = val
     12     return ordered_dict

<ipython-input-9-352b10801248> in recursively_order_list(l)
     14 def recursively_order_list(l):
     15     ordered_list = []
---> 16     for element in sorted(l):
     17         if isinstance(element, list):
     18             element = recursively_order_list(element)

TypeError: '<' not supported between instances of 'str' and 'list'

所以看起来 Python 无法对由字符串/数字和列表/字典组成的可迭代对象进行排序,因为它不知道从列表/字典中获取什么作为比较项。

与字符串/数字相比,我如何更改我的函数以便将列表/字典放在已排序可迭代的末尾/开头?

简而言之,我应该如何改变我的功能,让它把上面的dict_2变成这个(手工编辑的)sorted_dict_2

sorted_dict_2 =  
            "key1":"value1",
            "key2":"value2",
            "key3":
                    "key3_1":"value3_1",
                    "key3_2":[ [],[],"value3_2_1" ],
                    "key3_5":"value3_5",                    
                    ,            
            "key5":"value5",           
            "key8":
                    "key8_1":"value8_1",
                    "key8_2":"value8_2",
                    "key8_5":                                
                                "key8_5_2":[
                                            ,
                                            ,
                                            "value8_5_2_1",
                                            
                                            "key8_5_2_4_1":"value8_5_2_4_1",
                                            "key8_5_2_4_2":"value8_5_2_4_2",                                               
                                            "key8_5_2_4_5":"value8_5_2_4_5"
                                            ,                                                                                        
                                            ],
                                "key8_5_4":["value8_5_a", "value8_5_b", "value8_5_c"],
                            ,
                    
                    ,
            "key9":"value9",
         

【问题讨论】:

我认为如果你不能对你期望的类型施加任何限制,这将是困难的。例如,[ [],[],"value3_2_1" ] 是否也包含数字?这种情况应该怎么处理? 我错过了什么吗?在order_all_dicts_and_lists_in_iterable 内部,第一个if if isinstance(iterable1, dict):,但随后嵌套在内部,您检查if isinstance(iterable1, list):,但这永远不会是真的...... 谢谢胡安帕!我删除了无法访问的分支 【参考方案1】:

因此,基本上,您需要创建一个关键函数,使所有容器的比较比其他任何容器都少。一个方便的值是float('inf')。但是,由于我们不知道要排序的内容是包含数字还是字符串,所以我们必须将所有内容转换为元组,并手动映射每个字符串的序数值:map(ord, x)

如果您希望容器移到前面,以下是一个示例(所以 negative inf...:

from collections import OrderedDict

def recursively_order_dict(d):
    ordered_dict = OrderedDict()
    for key in sorted(d.keys()):
        val = d[key]
        if isinstance(val, dict):
            val = recursively_order_dict(val)
        if isinstance(val, list):
            val = recursively_order_list(val)
        ordered_dict[key] = val
    return ordered_dict

def _move_containers_to_end(x):
    if isinstance(x, (list, dict)):
        # to put at the end, use inf, at the start, -inf
        return (float('-inf'),)
    elif isinstance(x, str):
        return tuple(map(ord,  x))
    else: # assuming we only can get numbers at this point
        return (x,)

def recursively_order_list(l):
    ordered_list = []
    for element in sorted(l, key=_move_containers_to_end):
        if isinstance(element, list):
            element = recursively_order_list(element)
        if isinstance(element, dict):
            element = recursively_order_dict(element)
        ordered_list.append(element)
    return ordered_list

def order_all_dicts_and_lists_in_iterable(iterable1):
    if isinstance(iterable1, dict):
        ordered_iterable = recursively_order_dict(iterable1)
    elif isinstance(iterable1, list):
        ordered_iterable = recursively_orded_list(iterable1)
    else:
        print("%s\n is nor a list nor a dictionary.\nIts type is %s." % (iterable1, type(iterable1)) )
    return ordered_iterable

上面的结果是:

OrderedDict([('key1', 'value1'),
             ('key2', 'value2'),
             ('key3',
              OrderedDict([('key3_1', 'value3_1'),
                           ('key3_2', [[], [], 'value3_2_1']),
                           ('key3_5', 'value3_5')])),
             ('key5', 'value5'),
             ('key8',
              OrderedDict([('key8_1', 'value8_1'),
                           ('key8_2', 'value8_2'),
                           ('key8_5',
                            OrderedDict([('key8_5_2',
                                          [OrderedDict(),
                                           OrderedDict([('key8_5_2_4_1',
                                                         'value8_5_2_4_1'),
                                                        ('key8_5_2_4_2',
                                                         'value8_5_2_4_2'),
                                                        ('key8_5_2_4_5',
                                                         'value8_5_2_4_5')]),
                                           OrderedDict(),
                                           'value8_5_2_1']),
                                         ('key8_5_4',
                                          ['value8_5_a',
                                           'value8_5_b',
                                           'value8_5_c'])]))])),
             ('key9', 'value9')])

注意,您可能想要执行以下操作:

import sys:
if sys.version_info.minor < 7:
    OrderedMapping = dict
else:
    from collections import OrderedDict as OrderedMapping

然后使用:

ordered_dict = OrderedMapping()

recursively_order_dict

【讨论】:

以上是关于如何一次对字典或列表中的所有嵌套字典和列表进行排序?的主要内容,如果未能解决你的问题,请参考以下文章

列表中嵌套字典,根据字典的值排序

如何在 Linq C# 中使用嵌套字典对列表进行排序?

在Python列表中对嵌套字典进行排序? [复制]

不递归地访问嵌套列表和字典中的所有元素

列表嵌套字典,根据字典某一key排序

学不会的python之通过某几个关键字排序分组一个字典列表(列表中嵌套字典)