4

特定の条件に一致する辞書全体を再帰的に調べて削除する必要がある、ネストされたリストと辞書のツリーがあります。たとえば、「タイプ」が「フォルダ」で、子がない (または子の空のリスト) 辞書をすべて削除する必要があります。

私はまだ初心者のPythonistなので、力ずくで許してください。

簡単にコピー アンド ペーストできるように書式設定されたサンプル辞書を次に示します。

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

1 に答える 1

5

データ構造をたどり、各ノードで関数を呼び出す関数を作成することをお勧めします。

「繰り返しシーケンスからアイテムを削除する」バグを回避するために更新されました

例えば

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関数の 3 つの引数を使用します。
  • walk関数は、子ノードにアクセスした後にwork関数を呼び出します。
  • work関数は、子ノードと親ノードの両方を引数として受け取るため、子のプルーニングは次のように簡単です。parent['children'].remove(child)
  • 更新: コメントで気づいたように、反復中にシーケンスから削除すると、要素がスキップされます。for child in list(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'}
于 2013-03-30T21:40:51.180 に答える