Tree

The pyTooling.Tree package provides fast and simple tree data structure based on a single Node class.

Hint

This tree data structure outperforms anytree by far and even itertree by factor of 2.

Further alternatives:

treelib

treelib

Example Tree:

        %%{init: { "flowchart": { "nodeSpacing": 15, "rankSpacing": 30, "curve": "linear", "useMaxWidth": false } } }%%
graph TD
  R(Root)
  A(...)
  BL(Node); B(GrandParent); BR(Node)
  CL(Uncle); C(Parent); CR(Aunt)
  DL(Sibling); D(Node);  DR(Sibling)
  ELN1(Niece); ELN2(Nephew)
  EL(Child);   E(Child); ER(Child);
  ERN1(Niece);ERN2(Nephew)
  F1(GrandChild); F2(GrandChild)

  R:::mark1 --> A
  A --> BL & B & BR
  B --> CL & C & CR
  C --> DL & D & DR
  DL --> ELN1 & ELN2
  D:::cur --> EL & E & ER
  DR --> ERN1 & ERN2
  E --> F1 & F2

  classDef node fill:#eee,stroke:#777,font-size:smaller;
  classDef cur fill:#9e9,stroke:#6e6;
  classDef mark1 fill:#69f,stroke:#37f,color:#eee;
    

Root of the current node are marked in blue.

Comprehensive Example:

The following example code demonstrates a few features in a compact form:

# Create a new tree by creating a root node (no parent reference)
root = Node(value="OSVVM Regression Tests")

# Construct the tree top-down
lib = Node(value="Utility Library", parent=root)

# Another standalone node with unique ID (actually an independent tree)
common = Node(nodeID=5, value="Common")

# Construct bottom-up
axi = Node(value="AXI")
axiCommon = Node(value="AXI4 Common")
axi.AddChild(axiCommon)

# Group nodes and handover children at node creation time
vcList = [common, axi]
vcs = Node(value="Verification Components", parent=root, children=vcList)

# Add multiple nodes at once
axiProtocols = (
  Node(value="AXI4-Stream"),
  Node(value="AXI4-Lite"),
  Node(value="AXI4")
)
axi.AddChildren(axiProtocols)

# Create another standalone node and attach it later to a tree.
uart = Node(value="UART")
uart.Parent = vcs

The presented code will generate this tree:

OSVVM Regression Tests
├── Utility Library
├── Verification Components
    ├── Common
    ├── AXI
    │   ├── AXI4 Common
    │   ├── AXI4-Stream
    │   ├── AXI4-Lite
    │   ├── AXI4
    ├── UART

Features

  • Fast and simple tree data structure based on a single Node class.

  • A tree can be constructed top-down and bottom-up.

  • A node can have a unique ID.

  • A node knows its level (distance from root).

  • A node can have a value.

  • A node can store key-value-pairs via dictionary syntax.

  • A node has a reference to its parent node.

  • A node has a reference to the root node in a tree (representative node).

  • Rendering to simple ASCII art for debugging purposes.

Missing Features

  • Insert a node (currently, only add/append is supported).

  • Move a node in same hierarchy level.

  • Move node to a different level/node in the same tree in a single operation.

  • Allow node deletion.

Planned Features

  • Allow filters (predicates) in generators to allow node filtering.

  • Tree export to formats like GraphML, …

  • Export the tree data structure to file the YAML format.

  • Allow nodes to have tags and group nodes by tags.

  • Allow nodes to link to other nodes (implement proxy behavior?)

Out of Scope

  • Preserve or recover the tree data structure before an erroneous operation caused an exception and aborted a tree modification, which might leave the tree in a corrupted state.

  • Export the tree data structure to various file formats like JSON, TOML, …

  • Import a tree data structure from various file formats like JSON, YAML, TOML, …

  • Tree visualization or rendering to complex formats like GraphViz, Mermaid, …

By Feature

Danger

Accessing internal fields of a node is strongly not recommended for users, as it might lead to a corrupted tree data structure. If a power-user wants to access these fields, feel free to use them for achieving a higher performance, but you got warned 😉.

Unique ID

A node can be created with a unique ID when the object is created. Afterwards, the ID is a readonly property. Any hashable object can be used as an ID. The ID must be unique per tree. If trees are merged or nodes are added to an existing tree, the newly added node’s ID(s) are checked and might cause an exception.

# Create node with unique ID 5
node = Node(nodeID=5)

# Read a node's ID
nodeID = node.ID

Level

Each node has a level describing the distance from root node. It can be accessed via the read-only property Level.

The root node has a level of 0, children of root have a level of 1, and so on.

# Create node
root = Node(nodeID=0)
node2 = Node(nodeID=1, parent=root)

# Read a node's level
nodeLevel = node2.Level

Value

A node’s value can be given at node creating time or it can be set ant any later time via property Value. Any data type is accepted. The internally stored value can be retrieved by the same property. If a node’s string representation is requested via __str__() and a node’s value isn’t None, then the value’s string representation is returned.

# Create node with value 5
node = Node(value=5)

# Set or change a node's value
node.Value = 10

# Access a node's Value
value = node.Value

Key-Value-Pairs

Besides a unique ID and a value, each node can hold an arbitrary set of key-value-pairs.

# Create node
node = Node()

# Create or update a key-value-pair
node["key"] = value

# Access a value by key
value = node["key"]

# Delete a key-value-pair
del node["key"]

Parent Reference

Each node has a reference to its parent node. In case, the node is the root node, the parent reference is None. The parent-child relation can be set at node creation time, or a parent can be assigned to a node at any later time via property Parent. The same property can be used to retrieve the current parent reference.

# Create node without parent relation ship (root node)
root = Node(nodeID=0)

# Create a node add directly attach it to an existing tree
node = Node(nodeID=1, parent=root)

# Access a node's parent
parent = node.Parent

Merging Trees

In case, two trees were created (a single node is already a minimal tree), trees get merged if one tree’s root node is assigned a parent relationship.

# Create a tree with a single node
root = Node(nodeID=0)

# Create a second minimalistic tree
otherTree = Node(nodeID=100)

# Set parent relationship and merge trees
otherTree.Parent = root

See also

See Merging Trees for more details.

Splitting Trees

In case, a node within a tree’s hierarchy is updated with respect to it’s parent relationship to None, then the tree gets split into 2 trees.

# Create a tree of 4 nodes
root1 = Node(nodeID=0)
node1 = Node(nodeID=1, parent=root1)

root2 = Node(nodeID=2, parent=node1)
node3 = Node(nodeID=3, parent=root2)

# Split the tree between node1 and root2
root2.Parent = None

See also

See Splitting Trees for more details.

Moving a branch in same tree

Todo

TREE::Parent::move-branch in same tree - needs also testcases

Moving a branch to another tree

Todo

TREE::Parent::move-branch into another tree - needs also testcases

Root Reference

Each node has a reference to the tree’s root node. The root node can also be considered the representative node of a tree and can be accessed via read-only property Root.

When a node is assigned a new parent relation and this parent is a node in another tree, the root reference will change. (A.k.a. moving a branch to another tree.)

The root node of a tree contains tree-wide data structures like the list of unique IDs (_nodesWithID, _nodesWithoutID). By utilizing the root reference, each node can access these data structures by just one additional reference hop.

# Create a simple tree
root = Node()
nodeA = Node(parent=root)
nodeB = Node(parent=root)

# Check if nodeA and nodeB are in same tree
isSameTree = nodeA is nodeB

Path

The property Path returns a tuple describing the path top-down from root node to the current node.

# Create a simple tree representing directories
root = Node(value="C:")
dir = Node(value="temp", parent=root)
file = Node(value="test.log", parent=dir)

# Convert a path to string
path = "\".join(file.Path)

While the tuple returned by Path can be used in an iteration (e.g. a for-loop), also a generator is provided by method GetPath() for iterations.

# Create a simple tree representing directories
root = Node(value="C:")
dir = Node(value="temp", parent=root)
file = Node(value="test.log", parent=dir)

# Render path from root to node with indentations to ASCII art
for level, node in enumerate(file.GetPath()):
  print(f"{'  '*level}'\-'{node}")

# \-C:
#   \-temp
#     \-test.log

Ancestors

The method GetAncestors() returns a generator to traverse bottom-up from current node to the root node. If the top-down direction is needed, see Path for more details.

Python Code

Diagram

Tree Construction:

# Create an example tree
root =        Node(nodeID=0)
dots =        Node(nodeID=1, parent=root)
node1 =       Node(nodeID=2, parent=dots)
grandParent = Node(nodeID=3, parent=dots)
node2 =       Node(nodeID=4, parent=dots)
uncle =       Node(nodeID=5, parent=grandParent)
parent =      Node(nodeID=6, parent=grandParent)
aunt =        Node(nodeID=7, parent=grandParent)
sibling1 =    Node(nodeID=8, parent=parent)
me =          Node(nodeID=9, parent=parent)
sibling2 =    Node(nodeID=10, parent=parent)
niece1 =      Node(nodeID=11, parent=sibling1)
nephew1 =     Node(nodeID=12, parent=sibling1)
child1 =      Node(nodeID=13, parent=me)
child2 =      Node(nodeID=14, parent=me)
child3 =      Node(nodeID=15, parent=me)
niece2 =      Node(nodeID=16, parent=sibling2)
nephew2 =     Node(nodeID=17, parent=sibling2)
grandChild1 = Node(nodeID=18, parent=child2)
grandChild2 = Node(nodeID=19, parent=child2)

Usage

# Walk bottom-up all the way to root
for node in me.GetAncestors():
  print(node.ID)

Result

6   # parent
3   # grandparent
1   # ...
0   # root
        %%{init: { "flowchart": { "nodeSpacing": 15, "rankSpacing": 30, "curve": "linear", "useMaxWidth": false } } }%%
graph TD
  R(Root)
  A(...)
  BL(Node); B(GrandParent); BR(Node)
  CL(Uncle); C(Parent); CR(Aunt)
  DL(Sibling); D(Node);  DR(Sibling)
  ELN1(Niece); ELN2(Nephew)
  EL(Child);   E(Child); ER(Child);
  ERN1(Niece);ERN2(Nephew)
  F1(GrandChild); F2(GrandChild)

  R:::mark1 --> A
  A:::mark2 --> BL & B & BR
  B:::mark2 --> CL & C & CR
  C:::mark2 --> DL & D & DR
  DL --> ELN1 & ELN2
  D:::cur --> EL & E & ER
  DR --> ERN1 & ERN2
  E --> F1 & F2

  classDef node fill:#eee,stroke:#777,font-size:smaller;
  classDef cur fill:#9e9,stroke:#6e6;
  classDef mark1 fill:#69f,stroke:#37f,color:#eee;
  classDef mark2 fill:#69f,stroke:#37f;
    

Common Ancestors

If needed, method GetCommonAncestors() provides a generator to iterate the common ancestors of two nodes in a tree. It iterates from root node top-down until the common branch in the tree splits of.

Python Code

Diagram

Tree Construction:

# Create an example tree
root =        Node(nodeID=0)
dots =        Node(nodeID=1, parent=root)
node1 =       Node(nodeID=2, parent=dots)
grandParent = Node(nodeID=3, parent=dots)
node2 =       Node(nodeID=4, parent=dots)
uncle =       Node(nodeID=5, parent=grandParent)
parent =      Node(nodeID=6, parent=grandParent)
aunt =        Node(nodeID=7, parent=grandParent)
sibling1 =    Node(nodeID=8, parent=parent)
me =          Node(nodeID=9, parent=parent)
sibling2 =    Node(nodeID=10, parent=parent)
niece1 =      Node(nodeID=11, parent=sibling1)
nephew1 =     Node(nodeID=12, parent=sibling1)
child1 =      Node(nodeID=13, parent=me)
child2 =      Node(nodeID=14, parent=me)
child3 =      Node(nodeID=15, parent=me)
niece2 =      Node(nodeID=16, parent=sibling2)
nephew2 =     Node(nodeID=17, parent=sibling2)
grandChild1 = Node(nodeID=18, parent=child2)
grandChild2 = Node(nodeID=19, parent=child2)

Usage

# Walk bottom-up all the way to root
for node in nephew1.GetCommonAncestors(grandChild2):
  print(node.ID)

Result

0   # root
1   # ...
3   # grandparent
6   # parent
        %%{init: { "flowchart": { "nodeSpacing": 15, "rankSpacing": 30, "curve": "linear", "useMaxWidth": false } } }%%
graph TD
  R(Root)
  A(...)
  BL(Node); B(GrandParent); BR(Node)
  CL(Uncle); C(Parent); CR(Aunt)
  DL(Sibling); D(Node);  DR(Sibling)
  ELN1(Niece); ELN2(Nephew)
  EL(Child);   E(Child); ER(Child);
  ERN1(Niece);ERN2(Nephew)
  F1(GrandChild); F2(GrandChild)

  R:::mark1 --> A
  A:::mark2 --> BL & B & BR
  B:::mark2 --> CL & C & CR
  C:::mark2 --> DL & D & DR
  DL --> ELN1 & ELN2
  D --> EL & E & ER
  DR --> ERN1 & ERN2
  E --> F1 & F2
  ELN2:::cur; F2:::cur
  classDef node fill:#eee,stroke:#777,font-size:smaller;
  classDef cur fill:#9e9,stroke:#6e6;
  classDef mark1 fill:#69f,stroke:#37f,color:#eee;
  classDef mark2 fill:#69f,stroke:#37f;
    

Children

Children are all direct successors of a node.

A node object supports returning children either as a tuple via a property or as a generator via a method call.

Return a Tuple

Return a Generator

Children

Children

GetChildren()

Children and children thereof

— — — —

GetDescendants()

Python Code

Diagram

Tree Construction:

# Create an example tree
root =        Node(nodeID=0)
dots =        Node(nodeID=1, parent=root)
node1 =       Node(nodeID=2, parent=dots)
grandParent = Node(nodeID=3, parent=dots)
node2 =       Node(nodeID=4, parent=dots)
uncle =       Node(nodeID=5, parent=grandParent)
parent =      Node(nodeID=6, parent=grandParent)
aunt =        Node(nodeID=7, parent=grandParent)
sibling1 =    Node(nodeID=8, parent=parent)
me =          Node(nodeID=9, parent=parent)
sibling2 =    Node(nodeID=10, parent=parent)
niece1 =      Node(nodeID=11, parent=sibling1)
nephew1 =     Node(nodeID=12, parent=sibling1)
child1 =      Node(nodeID=13, parent=me)
child2 =      Node(nodeID=14, parent=me)
child3 =      Node(nodeID=15, parent=me)
niece2 =      Node(nodeID=16, parent=sibling2)
nephew2 =     Node(nodeID=17, parent=sibling2)
grandChild1 = Node(nodeID=18, parent=child2)
grandChild2 = Node(nodeID=19, parent=child2)

Usage

# Walk bottom-up all the way to root
for node in me.GetChildren():
  print(node.ID)

Result

13  # child1
14  # child2
15  # child3
        %%{init: { "flowchart": { "nodeSpacing": 15, "rankSpacing": 30, "curve": "linear", "useMaxWidth": false } } }%%
graph TD
  R(Root)
  A(...)
  BL(Node); B(GrandParent); BR(Node)
  CL(Uncle); C(Parent); CR(Aunt)
  DL(Sibling); D(Node);  DR(Sibling)
  ELN1(Niece); ELN2(Nephew)
  EL(Child);   E(Child); ER(Child);
  ERN1(Niece);ERN2(Nephew)
  F1(GrandChild); F2(GrandChild)

  R --> A
  A --> BL & B & BR
  B --> CL & C & CR
  C --> DL & D & DR
  DL --> ELN1 & ELN2
  D:::cur --> EL & E & ER
  DR --> ERN1 & ERN2
  E --> F1 & F2
  EL:::mark2; E:::mark2; ER:::mark2
  classDef node fill:#eee,stroke:#777,font-size:smaller;
  classDef cur fill:#9e9,stroke:#6e6;
  classDef mark1 fill:#69f,stroke:#37f,color:#eee;
  classDef mark2 fill:#69f,stroke:#37f;
    

Descendants

Descendants are all direct and indirect successors of a node (child nodes and child nodes thereof a.k.a. grandchild, grand-grandchildren, …).

A node object supports returning descendants as a generator via a method call to GetDescendants(), due to the recursive behavior.

See also

See Iterating a Tree for various other forms for iterating nodes in a tree.

Python Code

Diagram

Tree Construction:

# Create an example tree
root =        Node(nodeID=0)
dots =        Node(nodeID=1, parent=root)
node1 =       Node(nodeID=2, parent=dots)
grandParent = Node(nodeID=3, parent=dots)
node2 =       Node(nodeID=4, parent=dots)
uncle =       Node(nodeID=5, parent=grandParent)
parent =      Node(nodeID=6, parent=grandParent)
aunt =        Node(nodeID=7, parent=grandParent)
sibling1 =    Node(nodeID=8, parent=parent)
me =          Node(nodeID=9, parent=parent)
sibling2 =    Node(nodeID=10, parent=parent)
niece1 =      Node(nodeID=11, parent=sibling1)
nephew1 =     Node(nodeID=12, parent=sibling1)
child1 =      Node(nodeID=13, parent=me)
child2 =      Node(nodeID=14, parent=me)
child3 =      Node(nodeID=15, parent=me)
niece2 =      Node(nodeID=16, parent=sibling2)
nephew2 =     Node(nodeID=17, parent=sibling2)
grandChild1 = Node(nodeID=18, parent=child2)
grandChild2 = Node(nodeID=19, parent=child2)

Usage

# Walk bottom-up all the way to root
for node in me.GetDescendants():
  print(node.ID)

Result

13  # child1
14  # child2
18  # grandChild1
19  # grandChild2
15  # child3
        %%{init: { "flowchart": { "nodeSpacing": 15, "rankSpacing": 30, "curve": "linear", "useMaxWidth": false } } }%%
graph TD
  R(Root)
  A(...)
  BL(Node); B(GrandParent); BR(Node)
  CL(Uncle); C(Parent); CR(Aunt)
  DL(Sibling); D(Node);  DR(Sibling)
  ELN1(Niece); ELN2(Nephew)
  EL(Child);   E(Child); ER(Child);
  ERN1(Niece);ERN2(Nephew)
  F1(GrandChild); F2(GrandChild)

  R --> A
  A --> BL & B & BR
  B --> CL & C & CR
  C --> DL & D & DR
  DL --> ELN1 & ELN2
  D:::cur --> EL & E & ER
  DR --> ERN1 & ERN2
  E --> F1 & F2
  EL:::mark2; E:::mark2; ER:::mark2; F1:::mark2; F2:::mark2
  classDef node fill:#eee,stroke:#777,font-size:smaller;
  classDef cur fill:#9e9,stroke:#6e6;
  classDef mark1 fill:#69f,stroke:#37f,color:#eee;
  classDef mark2 fill:#69f,stroke:#37f;
    

Siblings

Siblings are all direct child nodes of a node’s parent node except itself.

A node object supports returning siblings either as tuples via a property or as a generator via a method call. Either all siblings are returned or just siblings left from the current node (left siblings) or right from the current node (right siblings). Left and right is based on the order of child references in the current node’s parent.

Sibling Selection

Return a Tuple

Return a Generator

Left Siblings

LeftSiblings

GetLeftSiblings()

All Siblings

Siblings

GetSiblings()

Right Siblings

RightSiblings

GetRightSiblings()

Attention

In case a node has no parent, an exception is raised, because siblings cannot exist.

Python Code

Diagram

Tree Construction:

# Create an example tree
root =        Node(nodeID=0)
dots =        Node(nodeID=1, parent=root)
node1 =       Node(nodeID=2, parent=dots)
grandParent = Node(nodeID=3, parent=dots)
node2 =       Node(nodeID=4, parent=dots)
uncle =       Node(nodeID=5, parent=grandParent)
parent =      Node(nodeID=6, parent=grandParent)
aunt =        Node(nodeID=7, parent=grandParent)
sibling1 =    Node(nodeID=8, parent=parent)
me =          Node(nodeID=9, parent=parent)
sibling2 =    Node(nodeID=10, parent=parent)
niece1 =      Node(nodeID=11, parent=sibling1)
nephew1 =     Node(nodeID=12, parent=sibling1)
child1 =      Node(nodeID=13, parent=me)
child2 =      Node(nodeID=14, parent=me)
child3 =      Node(nodeID=15, parent=me)
niece2 =      Node(nodeID=16, parent=sibling2)
nephew2 =     Node(nodeID=17, parent=sibling2)
grandChild1 = Node(nodeID=18, parent=child2)
grandChild2 = Node(nodeID=19, parent=child2)

Usage

# Walk bottom-up all the way to root
for node in me.GetLeftSiblings():
  print(node.ID)
for node in me.GetRightSiblings():
  print(node.ID)

Result

8   # sibling1
10  # sibling2
        %%{init: { "flowchart": { "nodeSpacing": 15, "rankSpacing": 30, "curve": "linear", "useMaxWidth": false } } }%%
graph TD
  R(Root)
  A(...)
  BL(Node); B(GrandParent); BR(Node)
  CL(Uncle); C(Parent); CR(Aunt)
  DL(Sibling); D(Node);  DR(Sibling)
  ELN1(Niece); ELN2(Nephew)
  EL(Child);   E(Child); ER(Child);
  ERN1(Niece);ERN2(Nephew)
  F1(GrandChild); F2(GrandChild)

  R --> A
  A --> BL & B & BR
  B --> CL & C & CR
  C --> DL & D & DR
  DL --> ELN1 & ELN2
  D:::cur --> EL & E & ER
  DR --> ERN1 & ERN2
  E --> F1 & F2
  DL:::mark2; DR:::mark2
  classDef node fill:#eee,stroke:#777,font-size:smaller;
  classDef cur fill:#9e9,stroke:#6e6;
  classDef mark1 fill:#69f,stroke:#37f,color:#eee;
  classDef mark2 fill:#69f,stroke:#37f;
    

Relatives

Relatives are siblings and their descendants.

A node object supports returning relatives as a generator via a method call, due to the recursive behavior. Either all relatives are returned or just relatives left from the current node (left relatives) or right from the current node (right relatives). Left and right is based on the order of child references in the current node’s parent.

Relative Selection

Return a Generator

Left Siblings

GetLeftRelatives()

All Siblings

GetRelatives()

Right Siblings

GetRightRelatives()

Attention

In case a node has no parent, an exception is raised, because siblings and therefore relatives cannot exist.

Python Code

Diagram

Tree Construction:

# Create an example tree
root =        Node(nodeID=0)
dots =        Node(nodeID=1, parent=root)
node1 =       Node(nodeID=2, parent=dots)
grandParent = Node(nodeID=3, parent=dots)
node2 =       Node(nodeID=4, parent=dots)
uncle =       Node(nodeID=5, parent=grandParent)
parent =      Node(nodeID=6, parent=grandParent)
aunt =        Node(nodeID=7, parent=grandParent)
sibling1 =    Node(nodeID=8, parent=parent)
me =          Node(nodeID=9, parent=parent)
sibling2 =    Node(nodeID=10, parent=parent)
niece1 =      Node(nodeID=11, parent=sibling1)
nephew1 =     Node(nodeID=12, parent=sibling1)
child1 =      Node(nodeID=13, parent=me)
child2 =      Node(nodeID=14, parent=me)
child3 =      Node(nodeID=15, parent=me)
niece2 =      Node(nodeID=16, parent=sibling2)
nephew2 =     Node(nodeID=17, parent=sibling2)
grandChild1 = Node(nodeID=18, parent=child2)
grandChild2 = Node(nodeID=19, parent=child2)

Usage

# Walk bottom-up all the way to root
for node in me.GetLeftRelatives():
  print(node.ID)
for node in me.GetRightRelatives():
  print(node.ID)

Result

8   # sibling1
11  # niece1
12  # nephew1

10  # sibling2
16  # niece2
17  # nephew2
        %%{init: { "flowchart": { "nodeSpacing": 15, "rankSpacing": 30, "curve": "linear", "useMaxWidth": false } } }%%
graph TD
  R(Root)
  A(...)
  BL(Node); B(GrandParent); BR(Node)
  CL(Uncle); C(Parent); CR(Aunt)
  DL(Sibling); D(Node);  DR(Sibling)
  ELN1(Niece); ELN2(Nephew)
  EL(Child);   E(Child); ER(Child);
  ERN1(Niece);ERN2(Nephew)
  F1(GrandChild); F2(GrandChild)

  R --> A
  A --> BL & B & BR
  B --> CL & C & CR
  C --> DL & D & DR
  DL --> ELN1 & ELN2
  D:::cur --> EL & E & ER
  DR --> ERN1 & ERN2
  E --> F1 & F2
  DL:::mark2; ELN1:::mark2; ELN2:::mark2; DR:::mark2; ERN1:::mark2; ERN2:::mark2
  classDef node fill:#eee,stroke:#777,font-size:smaller;
  classDef cur fill:#9e9,stroke:#6e6;
  classDef mark1 fill:#69f,stroke:#37f,color:#eee;
  classDef mark2 fill:#69f,stroke:#37f;
    

Iterating a Tree

A tree (starting at the root node) or a subtree (starting at any node in the tree) can be iterated in various orders:

Merging Trees

A tree B is merged into an existing tree A, when a tree B’s parent relation is set to a non-None value. Therefore use the B.Parent property and set it to A: B.Parent = A.

The following operations are executed on the tree B:

  1. register all nodes of B with and without ID in A, then

  2. delete the list and dictionary objects for nodes with and without IDs from B.

The following operations are executed on all nodes in tree B:

  • set root reference to A.

  • recompute the level within A.

Attention

In case a node’s ID already exists in A, an exception is raised, because IDs are unique.

Splitting Trees

Todo

TREE: splitting a tree

Tree Rendering

The tree data structure can be rendered as ASCII art. The Render() method renders the tree into a multi line string.

Todo

TREE:Render:: explain parameters

Example

<Root 0>
o-- <Node 1>
|   o-- <Node 4>
|   |   o-- <Node 8>
|   |       o-- <Node 9>
|   o-- <Node 5>
|       o-- <Node 10>
|           o-- <Node 11>
|           o-- <Node 12>
|           o-- <Node 13>
o-- <Node 2>
o-- <Node 3>
    o-- <Node 6>
    o-- <Node 7>

Competing Solutions

This tree data structure outperforms anytree by far and even itertree by factor of 2.

anytree

Source: anytree

Todo

TREE::anytree write comparison here.

Disadvantages

Standoff

Advantages

# add code here

itertree

Source: itertree

Todo

TREE::itertree write comparison here.

Disadvantages

Standoff

Advantages

# add code here

treelib

Source: treelib

Todo

TREE::treelib write comparison here.

Disadvantages

Standoff

Advantages

# add code here