如何在Python中一次性排序字典或列表内所有嵌套的字典和列表?
嵌套字典列表统一排序函数开发问题
我想要开发一款高效通用的函数,实现如下目标:
对字典或列表内部的所有嵌套字典、列表统一排序。
注意: 使用了collections.OrderedDict,目的是兼容Python 3.7之前的版本,这些版本的原生字典不保留插入顺序。
基于现有仅支持嵌套字典排序的递归函数,打算开发对应的仅支持嵌套列表排序的递归函数,再通过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) 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 = { "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", }
解决方案
核心思路是给sorted函数自定义排序键,给不同类型的元素分配优先级权重,让列表、字典的优先级高于普通字符串/数字,保证它们排在前面,同类型元素按原有规则排序。同时修正了原代码中并列判断导致的逻辑错误:
from collections import OrderedDict def get_sort_key(element): # 自定义排序权重:list < dict < 其他基础类型,保证容器排在前面 if isinstance(element, list): return (0, element) elif isinstance(element, dict): return (1, str(sorted(element.keys()))) else: return (2, element) 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) elif isinstance(val, list): val = recursively_order_list(val) ordered_dict[key] = val return ordered_dict def recursively_order_list(l): ordered_list = [] # 改用自定义key排序,避免跨类型比较错误 for element in sorted(l, key=get_sort_key): if isinstance(element, list): element = recursively_order_list(element) elif 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_order_list(iterable1) else: print("%s\n is not a list nor a dictionary.\nIts type is %s." % (iterable1, type(iterable1)) ) return return ordered_iterable
如果需要让列表/字典排在末尾,只需要把get_sort_key里的权重顺序反过来即可:基础类型权重最低,容器权重最高。
内容的提问来源于stack exchange,提问作者Tms91
相关产品推荐
相关产品推荐

