1

I have stumbled upon a problem whilst trying to walk my binary tree constructed using AnyNode from anytree.

First, I need to say that each node has a specific id associated with it, so that the root node has an id=0, its children id=1 and id=2. Then the children of id=1 are id=3 and id=4 and so on. I need to be able to walk the tree from top to bottom, check whether a condition is fulfilled on each node, and if it is, save the node and remove all its children from the walking process.

The way I do this is by making a list of the ids using

list1=[node.id for node in LevelOrderIter(root)]
listfar=[]

and then

i=0
while i<len(list1):
 ....
 if condition is fulfilled for a node named e.g parent:
   list2=[node.id for node in LevelOrderIter(parent)]
   listfar.append(list2)
   list1=list(set(list1).difference(list2))
   list1.sort()
   i=i-1
  i=i+1

The reason I have used node.id instead of just node is so that I can sort after the removal, since it doesn't keep my order. The main problem here is that LevelOrderIter takes a lot of time to run for my tree (since the tree has 21 levels). Also, I have not used a walk module since I need to do this for all the nodes in each level, not just from a node to a leaf.

For my case, it takes 1 minute to walk the entire tree and I need it to drop down to the order of a second at most.

Is there a module in anytree that produces the same list of node.ids with LevelOrderIter of the descendants of a node fast? If there isn't one, but there is a module that does that for the entire nodes (not just node.id) is there a way I can keep the order of the elements of list1 when removing the nodes?

1 Answers1

0

If I understand the problem correctly, you want to traverse the tree in level-order. If a node fullfills a certain condition, you want to store the node, but not traverse any of its descendants further.

There seems to be an easier way to accomplish this in anytree. The signature is:

LevelOrderIter(node, filter_=None, stop=None, maxlevel=None)

the stop parameter sounds very close to what you want.

So one way to write your problem is as follows:

list1 = list(LevelOrderIter(node, stop=lambda node: not node.is_root and condition(node.parent))

If that's not fast enough, we can become a bit tricky:

list1 = []
def stop_and_append(node):
    if condition(node):
        list1.append(node)
        return True
    else:
        return False

for _ in LevelOrderIter(node, stop=stop_and_append):
    pass  # Run for side-effect

But for a binary tree, the improvement should be marginal and the one-liner looks more readable.

If that's still not fast enough, you could try another tree package, like mine, but to be honest, I don't expect much speed-up anymore since your tree is only 20 levels deep.

list1 = []
def keep_or_append(node, _):
    if condition(node):
        list1.append(node)
        return False
    else:
        return True

for _ in node.iter_tree(keep_or_append, order='level'):
    pass  # Run for side-effect

# or short version:

list1 = list(node.iter_tree(keep=lambda node, _: node.is_root or not condition(node.parent), order='level'))