遍历和修改类似于dict结构的树状列表

时间:2010-12-06 23:37:29

标签: python

我有一个看起来像这样的结构:

[ {'id': 4, 'children': None},
  {'id': 2, 'children': 
    [ {'id': 1, 'children':
        [ {'id': 6, 'children': None},
          {'id': 5, 'children': None} ]
      },
      {'id': 7, 'children':
        [ {'id': 3, 'children': None} ]
      }
    ]
  }
]

我还有一个选定ID列表[4, 5, 6, 7]。我想遍历列表,并且对于列表中的每个对象,如果选中,则添加selected键,其值为1,如果不是,则添加0

目前我正在使用此函数递归执行此操作:

def mark_selected(tree, selected):
    for obj in tree:
        obj['selected'] = 1 if obj['id'] in selected else 0
        if obj['children'] is not None:
            obj['children'] = mark_selected(obj['children'], selected)
    return tree

这似乎工作正常,但我想知道是否有更聪明的方法来做到这一点,可能使用列表理解或生成器。

有人能为此提出更优雅的解决方案吗?

3 个答案:

答案 0 :(得分:7)

递归非常优雅。列表推导不适用,因为您正在改变结构,而不是生成新的序列。对于生成器,您可以编写DFS或BFS遍历器。

def dfs(nodes):
    if nodes is not None:
        for node in nodes:
            yield node
            for child in dfs(node['children']):
                yield child

for node in dfs(tree):
    if node['id'] in selected:
        node['selected'] = true

如果要选择的ID列表很大,将其转换为ID为密钥的dict会更高效,这将加快查找速度(node['id'] in selected)。

selected = dict(zip(selected, selected))

答案 1 :(得分:2)

由于您通过修改输入对象来操作,并且由于对象在Python中具有引用语义,因此您无需在递归步骤中返回值或使用返回值。此外,如果您可以用'[]'替换子项的'None'条目(更好的是,使用整个而不是列表中的元组),那么您可以简化逻辑 - 您根本不需要基本情况​​,那么,因为你可以递归到一个“空树”,它只会为所有零项运行for循环,即什么都不做 - 这就是你想要的。

和FFS,你为什么不使用Python的内置布尔类型?

def mark_selected(tree, selected):
    for obj in tree:
        obj['selected'] = obj['id'] in selected
        mark_selected(obj['children'], selected)

(哦,你甚至需要让孩子按照特定的顺序保留吗?有一个包含'id'键的dicts列表是不自然的;有一个dict,其中键是id的更有意义,值为没有'id'的dicts。)

答案 2 :(得分:2)

我喜欢使用闭包来表示递归函数,对于这个例子它并不重要,但你可以节省在递归调用中传递'selected'的需要。在更复杂的示例中,您可以在包含函数中保留相当多的状态以供递归使用。

def mark_selected(tree, selected):
    def recur(tree):
        for obj in tree:
                obj['selected'] = 1 if obj['id'] in selected else 0
                if obj['children'] is not None:
                    recur(obj['children'])
    recur(tree)