【问题标题】:Python: Search for and delete nested lists of dictionariesPython:搜索和删除字典的嵌套列表
【发布时间】:2013-03-21 08:35:21
【问题描述】:

我有一个嵌套列表和字典树,我需要递归地遍历并删除符合特定条件的整个字典。例如,我需要删除所有“类型”为“文件夹”且没有子级(或空的子级列表)的字典。

我仍然是 Python 初学者,所以请原谅我的蛮力。

这是一个示例字典,其格式便于复制和粘贴。

{'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)

【问题讨论】:

  • 1) 你不需要isinstance,如果你能从dicttype属性中算出来的话; 2) 在 Python 中,习惯上“请求宽恕,而不是许可”,这意味着在访问之前检查内容是否在 dict 中是不好的风格——它会使代码混乱并创建不必要的嵌套级别。你应该抓住KeyError。同样,这可能会因为不是一个真正的问题而被关闭——您没有指定问题,基本上只是要求我们编写您的代码。
  • 1) 感谢您的建议。将来我将使用 try 块而不是测试键的存在。 2)我认为问题很清楚。我需要删除所有没有子节点或为空子节点的节点。如果我要求其他人编写我的代码,我就不会提供我的函数并寻求帮助。

标签: python list dictionary nested


【解决方案1】:

我建议您编写一个函数来遍历您的数据结构并在每个节点上调用一个函数。

已更新以避免“从迭代序列​​中删除项目”错误

例如

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 函数将子节点和父节点都作为参数,因此修剪子节点就像parent['children'].remove(child) 一样简单
  • 更新:正如在 cmets 中所注意到的,如果您在迭代时从序列中删除,它将跳过元素。 walk 函数中的 for child in list(node.get('children',[])) 复制子列表,允许从父键中删除条目而不跳过。

那么

>>> 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'}

【讨论】:

  • 不幸的是,这不是一个正确的解决方案——尽管很优雅:我注意到应该删除group-11——它是一个Folder并且没有孩子?
  • 这个:and len(node.get('children',[])) == 0and not node.get('children')
  • @Theodoros Zelleke:那是因为它没有被访问过。给removeEmptyFolders添加打印语句:>>> walk(d,func=removeEmptyFolders) group-1 group-3 dc-1 root
  • @TheodrosZelleke:感谢您指出这一点。快速修复walk 函数。
  • 这段代码很漂亮,而且比我尝试做的要简单得多。做得很好!谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多