Python:搜索和删除嵌套字典列表
我有一个嵌套列表和字典的树,我需要递归地遍历并删除符合特定条件的整个字典。例如,我需要删除“类型”为“文件夹”且没有子项(或子项列表为空)的所有词典 我仍然是一个初学蟒蛇的人,所以请原谅我的蛮力 这里有一个示例字典,其格式便于复制和粘贴Python:搜索和删除嵌套字典列表,python,list,dictionary,nested,Python,List,Dictionary,Nested,我有一个嵌套列表和字典的树,我需要递归地遍历并删除符合特定条件的整个字典。例如,我需要删除“类型”为“文件夹”且没有子项(或子项列表为空)的所有词典 我仍然是一个初学蟒蛇的人,所以请原谅我的蛮力 这里有一个示例字典,其格式便于复制和粘贴 {'children': [{'children': [{'key': 'group-1', 'name': 'PRD', 'parent': 'dc-1',
{'children': [{'children': [{'key': 'group-1',
'name': 'PRD',
'parent': 'dc-1',
'type': 'Folder'},
{'children': [{'key': 'group-11',
'name': 'App1',
'parent': 'group-2',
'type': 'Folder'}],
'key': 'group-2',
'name': 'QA',
'parent': 'dc-1',
'type': 'Folder'},
{'key': 'group-3',
'name': 'Keep',
'parent': 'dc-1',
'type': 'Host'}],
'key': 'dc-1',
'name': 'ABC',
'parent': 'root',
'type': 'Datacenter'}],
'key': 'root',
'name': 'Datacenters',
'parent': None,
'type': 'Folder'}
在本词典中,唯一应该保留的树是/root/dc-1/group-3。应首先删除group-11文件夹,然后删除其父文件夹(因为子文件夹已不存在),等等
我尝试过许多不同的递归方法,但似乎无法使其正常工作。任何帮助都将不胜感激
def cleanup(tree):
def inner(tree):
if isinstance(tree, dict):
if 'type' in tree and tree['type'] == 'Folder':
if 'children' not in tree or not tree['children']:
print 'Deleting tree: ' + str(tree['name'])
if str(tree['key']) not in del_nodes:
del_nodes.append(str(tree['key']))
else:
for item in tree.values():
inner(item)
# Delete empty folders here
if del_nodes:
print 'Perform delete here'
if 'children' in tree and isinstance(tree['children'], (list, tuple)):
getvals = operator.itemgetter('key')
tree['children'].sort(key=getvals)
result = []
# groupby is the wrong method. I need a list of tree['children'] that doesn't contain keys in del_nodes
for k, g in itertools.groupby(tree['children'], getvals):
result.append(g.next())
tree['children'][:] = result
del_nodes = []
else:
for item in tree.values():
inner(item)
elif isinstance(tree, (list, tuple)):
for item in tree:
inner(item)
if isinstance(item, dict):
if 'type' in item and item['type'] == 'Folder':
if 'children' not in item or not item['children']:
print 'Delete ' + str(item['name'])
if str(item['key']) not in del_nodes:
del_nodes.append(str(item['key']))
elif isinstance(item, (list, tuple)):
if not item:
print 'Delete ' + str(item['name'])
if str(item['key']) not in del_nodes:
del_nodes.append(str(item['key']))
inner(tree)
我建议您编写一个函数来遍历您的数据结构,并在每个节点上调用一个函数 更新以避免“从迭代序列中删除项”错误 例如
def walk(node,parent=None,func=None):
for child in list(node.get('children',[])):
walk(child,parent=node,func=func)
if func is not None:
func(node,parent=parent)
def removeEmptyFolders(node,parent):
if node.get('type') == 'Folder' and len(node.get('children',[])) == 0:
parent['children'].remove(node)
d = {'children': [{'children': [{'key': 'group-1',
'name': 'PRD',
'parent': 'dc-1',
'type': 'Folder'},
{'children': [{'key': 'group-11',
'name': 'App1',
'parent': 'group-2',
'type': 'Folder'}],
'key': 'group-2',
'name': 'QA',
'parent': 'dc-1',
'type': 'Folder'},
{'key': 'group-3',
'name': 'Keep',
'parent': 'dc-1',
'type': 'Host'}],
'key': 'dc-1',
'name': 'ABC',
'parent': 'root',
'type': 'Datacenter'}],
'key': 'root',
'name': 'Datacenters',
'parent': None,
'type': 'Folder'}
注释
- Walk函数使用三个参数:子节点、父节点和work函数
- walk函数在访问子节点后调用work函数
- work函数将子节点和父节点都作为参数,因此修剪子节点就像修剪父节点['children']一样简单。删除(子节点)
- 更新:正如注释中所注意到的,如果在迭代时从序列中删除,它将跳过元素
在用于列表中的子项(node.get('children',[])
函数中复制子项列表,允许在不跳过的情况下从父项的键中删除条目walk
>>> walk(d,func=removeEmptyFolders)
>>> from pprint import pprint
>>> pprint(d)
{'children': [{'children': [{'key': 'group-3',
'name': 'Keep',
'parent': 'dc-1',
'type': 'Host'}],
'key': 'dc-1',
'name': 'ABC',
'parent': 'root',
'type': 'Datacenter'}],
'key': 'root',
'name': 'Datacenters',
'parent': None,
'type': 'Folder'}
1) 如果可以从
dict
的type
属性中看出这一点,则不需要isinstance
;2) 在Python中,习惯上是“请求原谅,而不是许可”,这意味着在访问之前检查dict中是否有内容是一种糟糕的风格——它会使代码变得混乱,并创建不必要的嵌套级别。您应该只捕获键错误
。另外,照目前的情况,这可能会被视为不是一个真正的问题——您还没有指定问题,基本上只是要求我们编写您的代码。1)感谢您的建议。我将使用try块,而不是在将来测试密钥的存在。2) 我觉得问题很清楚。我需要删除所有没有子节点或子节点为空的节点。如果我让别人写我的代码,我就不会提供我的函数并寻求帮助。不幸的是,这不是一个非常正确的解决方案——尽管很优雅:我注意到group-11
应该被删除——它是一个文件夹
,没有子项?这是:和len(node.get('children',[])==0
是而不是节点。get('children')
@Theodoros Zelleke:那是因为它没有被访问。将打印语句添加到removemptyfolders
:>>walk(d,func=removemptyfolders)group-1 group-3 dc-1 root
@TheodrosZelleke:感谢您指出这一点。快速修复walk
功能。此代码很漂亮,比我尝试做的要简单得多。做得很好!非常感谢。