I'm using anytree to generate a tree from a file, Tree.txt
. Each indent is 3 spaces (
). Here's what my file looks like:
ROOT
Node1
Node2
Node3
Node4
Node5
Node6
And here's what I have so far for the generating code:
from anytree import Node, RenderTree, find_by_attr
def parse_tree():
with open('Tree.txt', 'r') as file:
file_contents = file.readlines()
root = Node("ROOT")
current_indent = -1 # store index for comparing later
for (index, line) in enumerate(file_contents):
leading_spaces = len(line) - len(line.lstrip(' '))
indent = int(leading_spaces / 3) # indent = 3 spaces
if indent > current_indent: # node must be 1 level down
# get the previous Node (1 level up)
previous_line = file_contents[index - 1].strip()
current_line = line.strip()
parent = find_by_attr(root, previous_line)
Node(current_line, parent=parent) # start searching from top node (root) for `previous_line`
current_indent = indent
else: # node is on same level or higher
print(f"What to do for {line.strip()}?")
# what should I do here?
print(RenderTree(root)) # print the output
parse_tree()
However, this prints out:
What to do for Node5?
What to do for Node6?
Node('/ROOT')
└── Node('/ROOT/Node1')
└── Node('/ROOT/Node1/Node2')
└── Node('/ROOT/Node1/Node2/Node3')
└── Node('/ROOT/Node1/Node2/Node3/Node4')
The generated tree is fine, but only up until Node4
- Node5
and Node6
are missing. This is expected, because I didn't handle the case when the current indent is smaller than the previous indent (see # what should I do here?
).
What should I do when the current indent is smaller than the previous indent? I know I need to go up n
level higher, but how do I find out which level that is?
You'll need a kind of stack to keep track of the depth of the nodes, then you can use the number of initial spaces to determine which parent node to append each leaf to. E.g., three spaces means one level down, so you need to append to the root node at index 0 in the stack. (You could also say that by using a stack we are taking the absolute depth of the leaves, while you tried to solve it relatively if I'm making any sense). I suspect if you use find_by_attr
, that might not work in case there are leaves with the same name.
treestring = '''
ROOT
Node1
Node2
Node3
Node4
Node5
Node6'''.strip()
leaves = treestring.splitlines()
# initialize stack with the root node
stack = {0: Node(leaves.pop(0))}
for leaf in leaves:
# determine the node's depth
leading_spaces = len(leaf)-len(leaf.lstrip(' '))
level = int(leading_spaces/3)
# add the node to the stack, set as parent the node that's one level up
stack[level] = Node(leaf.strip(), parent=stack[level-1])
tree = stack[0]
for pre, _, node in RenderTree(tree):
print(f"{pre}{node.name}")
Result:
ROOT
├── Node1
│ └── Node2
│ └── Node3
│ └── Node4
├── Node5
└── Node6