尝试过滤嵌套字典。我的解决方案很笨重,希望看看是否有更好的方法使用了解。只对这个例子的字典和列表感兴趣。
_dict_key_filter()将过滤嵌套字典的键或嵌套字典列表。任何不在obj_filter中的内容都将在所有嵌套级别上被忽略。
obj:可以是字典或字典列表。
obj_filter:必须是过滤值列表
def _dict_key_filter(self, obj, obj_filter):
if isinstance(obj, dict):
retdict = {}
for key, value in obj.iteritems():
if key in obj_filter:
retdict[key] = copy.deepcopy(value)
elif isinstance(value, (dict, list)):
child = self._dict_key_filter(value, obj_filter)
if child:
retdict[key] = child
return retdict if retdict else None
elif isinstance(obj, list):
retlist = []
for value in list:
child = self._dict_key_filter(value, obj_filter)
if child:
retlist.append(child)
return retlist if retlist else None
else:
return None
Example#
dict1 = {'test1': {'test2':[1,2]}, 'test3': [{'test6': 2},
{'test8': {'test9': 23}}], 'test4':{'test5': 5}}
filter = ['test5' , 'test9']
return = _dict_key_filter(dict1, filter)
return value would be {'test3': [{'test8': {'test9': 23}}], 'test4': {'test5': 5}}
答案 0 :(得分:1)
这是一个非常老的问题。我最近遇到了类似的问题。
这也许很明显,但是您正在处理一棵树,其中每个节点都有任意数量的子代。您想要剪切不包含某些项目的子树作为节点(不是叶子)。为此,您使用了自定义DFS:main函数返回子树或None
。如果值为None
,则“剪切”分支。
首先,函数dict_key_filter
返回一个(非空)dict
,一个(非空)list
或None
(如果未在其中找到过滤键)分支。
为了降低复杂度,在每种情况下都可以返回sequence:如果未找到过滤键,则返回空序列;如果仍在搜索或找到树的叶子,则返回非空序列。您的代码如下:
def dict_key_filter(obj, obj_filter):
if isinstance(obj, dict):
retdict = {}
...
return retdict # empty or not
elif isinstance(obj, list):
retlist = []
...
return retlist # empty or not
else:
return [] # obvioulsy empty
这是容易的部分。现在我们必须填充点。
list
案让我们从list
案例开始,因为它很容易重构:
retlist = []
for value in obj:
child = dict_key_filter0(value, obj_filter)
if child:
retlist.append(child)
我们可以将其转换为简单的列表理解:
retlist = [dict_key_filter(value, obj_filter) for value in obj if dict_key_filter(value, obj_filter)]
缺点是dict_key_filter
被评估两次。我们可以通过一些技巧来避免这种情况(请参阅https://stackoverflow.com/a/15812866):
retlist = [subtree for subtree in (dict_key_filter(value, obj_filter) for value in obj) if subtree]
内部表达式(dict_key_filter(value, obj_filter) for value in obj)
是一个生成器,每个值调用一次dict_key_filter
。但是,如果我们构建dict_key_filter
的闭包,我们甚至可以做得更好:
def dict_key_filter(obj, obj_filter):
def inner_dict_key_filter(obj): return dict_key_filter(obj, obj_filter)
...
retlist = list(filter(len, map(inner_dict_key_filter, obj)))
现在我们进入了函数世界:map
将inner_dict_key_filter
应用于列表的每个元素,然后子树将被过滤以排除空子树({{1}是正确的,如果{{1 }}不为空。现在,代码如下:
len(subtree)
如果您熟悉函数式编程,那么subtree
的情况是可读的(不像Haskell那样可读,但仍然可读)。
def dict_key_filter(obj, obj_filter):
def inner_dict_key_filter(obj): return dict_key_filter(obj, obj_filter)
if isinstance(obj, dict):
retdict = {}
...
return retdict
elif isinstance(obj, list):
return list(filter(len, map(inner_dict_key_filter, obj)))
else:
return []
案我不会忘记您的问题中的list
标签。第一个想法是创建一个函数以返回分支的完整副本或DFS其余部分的结果。
dict
与dictionary-comprehension
情况一样,我们暂时不拒绝空白的def build_subtree(key, value):
if key in obj_filter:
return copy.deepcopy(value) # keep the branch
elif isinstance(value, (dict, list)):
return inner_dict_key_filter(value) # continue to search
return [] # just an orphan value here
:
list
我们现在有一个完美的字典理解案例:
subtree
同样,我们使用小技巧来避免两次计算值:
retdict = {}
for key, value in obj.items():
retdict[key] = build_subtree(key, value)
但是这里我们有一个小问题:上面的代码并不等同于原始代码。如果值为retdict = {key: build_subtree(key, value) for key, value in obj.items() if build_subtree(key, value)}
怎么办?在原始版本中,我们有retdict = {key:subtree for key, subtree in ((key, build_subtree(key, value)) for key, value in obj.items()) if subtree}
,但在新版本中,我们什么都没有。 0
值被评估为false并被过滤。然后,该字典可能会变空,并且我们错误地剪切了该分支。我们需要进行另一项测试以确保我们要删除一个值:如果它是空列表或字典,则将其删除,否则保留它:
retdict[key] = copy.deepcopy(0)
也就是说:
0
如果您还记得一些逻辑(https://en.wikipedia.org/wiki/Truth_table#Logical_implication),可以将其解释为:如果def to_keep(subtree): return not (isinstance(subtree, (dict, list)) or len(subtree) == 0)
是字典或列表,则它不能为空。
让我们放在一起:
def to_keep(subtree): return not isinstance(subtree, (dict, list)) or subtree
我不知道这是否更pythonic,但是对我来说似乎更清楚。
subtree