使用Python

时间:2018-04-20 21:16:43

标签: python json recursion tree

拥有以下json:

{
    'a': {
        'children': [],
        'name': 'a'
    },
    'b': {
        'children': [{
                'x': {
                    'children': [],
                    'name': 'x'
                }
            }, {
                'y': {
                    'children': [{
                        'z': {
                            'children': [],
                            'name': 'z'
                        }
                    }]
                }]
        }
    }

最终结果应为:

a 
b -> x
b -> y -> z

我无法解决我需要解决此问题的递归函数。链接列表是否为此解决方案?我的数据中存在未知级别的递归,因此该函数应该继续返回任何子节点。我没有问题递归地列出所有节点,但是,跟踪它们是我的问题。

def print_tree(tree, prev=None, child=False):
    for node in tree:
        print(node['name'])
        if len(node['children']):          
            print_tree(node['children'])




print_tree(tree_data)

我在这里缺少什么逻辑来跟踪这个?

2 个答案:

答案 0 :(得分:2)

您的代码存在很多问题

  1. JSON无效,在最后}之前错过了结束]

  2. 您的by个节点没有设置name

  3. 您的结构不一致:数据的每个项目都是一个节点,children中的每个元素都是一个节点,但您的数据本身不是一个节点。此外,您的最外层数据使用{ 'a': ..., 'b': ... }结构,但子项使用[ { 'a': ... }, { 'b': ... } ]结构。

  4. dict-wrapping节点使得很难将实际的节点输出。也就是说,如果我向您{ 'x': nodeX }提供'x'未知值,则您的程序很难提取nodeX

  5. 我们首先修复 1 2

    data = \
      { 'a': { 'children': []
             , 'name': 'a'
             }
      , 'b': { 'children': [ { 'x': { 'children': []
                                    , 'name': 'x'
                                    }
                             }
                           , { 'y': { 'children': [ { 'z': { 'children': []
                                                           , 'name': 'z'
                                                           }
                                                    }
                                                  ]
                                    , 'name': 'y' # you missed this
                                    }
                             } # you missed this
                           ]
              , 'name': 'b'  # you missed this
              }
      }
    

    然后我们通过使用root节点构建统一结构来修复 3

    root = \
      { 'root': { 'children': [ {k:v} for (k,v) in data.items() ]
                , 'name': 'root'
                }
      }
    

    然后我们使用unwrap_node帮助

    修复 4
    def unwrap_node (wrapped_node):
      node, *_ = wrapped_node.values()
      if 'children' in node and len (node['children']) > 0:
        return { 'name': node['name']
               , 'children': [ unwrap_node(n) for n in node['children'] ]
               }
      else:
        return node 
    

    现在我们了解你的问题。我们编写了一个通用的traverse函数,它只为树中的每个节点生成一个祖先路径( list

    def traverse (node, path = []):
      if 'children' in node and len (node['children']) > 0:
        for n in node['children']:
          yield from traverse (n, path + [ node ])
      else:
        yield path + [ node ]
    

    使用每个祖先路径,我们可以通过name属性轻松加入节点,并使用"->"

    分开
    for path in traverse (unwrap_node (root)):
      print (" -> ".join (node['name'] for node in path))
    
    # root -> a
    # root -> b -> x
    # root -> b -> y -> z
    

    最后,与上面的循环类似,实现所需的输出写入print_tree。我们也可以过滤掉root -> ...的打印

    def print_tree (node):    
      for path in traverse (unwrap_node (node)):
        print (" -> ".join (n['name'] for n in path if n['name'] is not 'root'))
    
    print_tree (root)
    # a
    # b -> x
    # b -> y -> z
    

    如果您解决了JSON的严重结构问题,则可以避免必须处理解决方法

答案 1 :(得分:1)

如果我这样做,我会收集列表中的路径,然后构建字符串。这样做的好处是可以轻松地更改您想要对这些路径执行的操作(例如,更改输出格式,将它们传递给另一个函数等),而无需更改逻辑。

为此,我将创建一个帮助函数来处理构建路径并具有我计划调用的函数,只需收集/转换结果。如下所示:

# this function collects the paths as lists (e.g. ['b', 'y', 'z']) and returns a list of those paths
def get_paths(tree):
  paths = []
  for branch in tree:
    name = tree[branch]['name']
    children = tree[branch]['children']
    if len(children):
      # this mostly accounts for the fact that the children are a dictionary in a list
      for node in children:
        # get the paths from the children
        sub_paths = get_paths(node)
        # add this element to the beginning of those paths
        for path in sub_paths:
          path.insert(0, name)
        # transfer modified sub-paths to list of paths
        paths.extend(sub_paths)
    else:
      # leaf node, add as a path with one element
      paths.append([name])
  return paths

# this function uses the above function to get the paths and then prints the results as desired
def print_tree(tree):
  paths = get_paths(tree)
  print(paths)
  # do whatever you want with the paths
  for path in paths:
    print(' -> '.join(path))

对于您的输入(修改为为' y'和' b'添加名称),提供:

a
b -> x
b -> y -> z