2

I'm using anytree currently to generate my search tree, how do I get all possible branch starting from the root node in list format

from anytree import Node, RenderTree, AsciiStyle

f = Node("f")
b = Node("b", parent=f)
a = Node("a", parent=b)
d = Node("d", parent=b)
c = Node("c", parent=d)
e = Node("e", parent=d)
g = Node("g", parent=f)
i = Node("i", parent=g)
h = Node("h", parent=i)
print(RenderTree(f, style=AsciiStyle()).by_attr())

Current Tree:

f 
|-- b
|   |-- a
|   +-- d
|       |-- c
|       +-- e
+-- g
    +-- i
        +-- h

wanted output (treeBranch):

[[f,b,a], [f,b,d,c], [f,b,d,e], [f,g,i,h]]

I'm not sure if there is a better way of doing this, open to any suggestion.

I want to use this list to check if a new path from user exists in the tree, for example:

newPath = [f, b]

for branch in treeBranch:
    if newPath in branch:
        return true
    else:
        // add new path to tree
ryanhz
  • 60
  • 2
  • 7

3 Answers3

6

You want to have the root path for every leaf node. Just use the PreOrderIter with a filter_ to retrieve the leaf nodes:

print(list(PreOrderIter(f, filter_=lambda node: node.is_leaf)))
[a, c, e, h]

And then access the path attribute on every node:

print([list(leaf.path) for leaf in PreOrderIter(f, filter_=lambda node: node.is_leaf)])
[[f,b,a], [f,b,d,c], [f,b,d,e], [f,g,i,h]]

If you like to have the path from any node in the tree towards the leaf nodes:

def allpaths(start):
    skip = len(start.path) - 1
    return [leaf.path[skip:] for leaf in PreOrderIter(start, filter_=lambda node: node.is_leaf)]
print(allpaths(b))
[(b, a), (b, d, c), (b, d, e)]

Please note that there is also a Walker, which serves the path from any node to another one.

c0fec0de
  • 651
  • 8
  • 4
  • Hey, the above solution does not seem to be working as so in anytree (2.8.0). Only `print(list(PreOrderIter(f, filter_=lambda node: node.is_leaf))) ` is giving me all the branches in a list but they are as a Node Object, for ex: [Node('/f/b/a'), Node('/f/b/d/c'), Node('/f/b/d/e'), Node('/f/g/i/h')] How may I get the data out of the node object in the form: [[f,b,a], [f,b,d,c], [f,b,d,e], [f,g,i,h]] ? – Michael Westen Dec 29 '21 at 13:20
0

It looks like the solution from @c0fec0de does not work like that anymore in anytree 2.8.0. I tried the following:

from anytree import Node, RenderTree, AsciiStyle, PreOrderIter
f = Node("f")
b = Node("b", parent=f)
a = Node("a", parent=b)
d = Node("d", parent=b)
c = Node("c", parent=d)
e = Node("e", parent=d)
g = Node("g", parent=f)
i = Node("i", parent=g)
h = Node("h", parent=i)
print(RenderTree(f, style=AsciiStyle()).by_attr())

Which gave me the following tree:

f
|-- b
|   |-- a
|   +-- d
|       |-- c
|       +-- e
+-- g
    +-- i
        +-- h

After this, to get a list of all the branches in the tree I tried:

>>> print(list(PreOrderIter(f, filter_=lambda node: node.is_leaf)))
[Node('/f/b/a'), Node('/f/b/d/c'), Node('/f/b/d/e'), Node('/f/g/i/h')]

And since the response is in a Node Object, to convert it to a list of nodes as string, I did the following:

>>> lst = list(PreOrderIter(f, filter_=lambda node: node.is_leaf))
>>> print(lst)
[Node('/f/b/a'), Node('/f/b/d/c'), Node('/f/b/d/e'), Node('/f/g/i/h')]
>>> f = [str(i)[7:-2].split(i.separator) for i in lst]
>>> print(f)
[['f', 'b', 'a'], ['f', 'b', 'd', 'c'], ['f', 'b', 'd', 'e'], ['f', 'g', 'i', 'h']]

Would be keen to see if there are better ways to do the same.

Michael Westen
  • 169
  • 2
  • 10
0

@michael-westen my solution to exploit fully .name and .path and to avoid strings manipulation:

leaves = [node_i.name for node_i in PreOrderIter(f.root, filter_=lambda node: node.is_leaf)]

branches = [[node_i.name for node_i in data_i.path] for data_i in PreOrderIter(f.root, filter_=lambda node: node.is_leaf)]

Davide
  • 116
  • 7