Tree
Tree node is very simple tree to record parent-children relationships.
You can store any type as a node. When you insert a value, the tree
assigns it a unique Int ID and gives it an empty children array. You
then wire up parent-child relationships by appending child IDs to a
parent. This makes it easy to build trees incrementally.
You can have multiple roots. A root is simply a node that is not a child of another node.
Since everything is keyed by ID, lookups, updates, and removals are straightforward. You can walk the tree depth-first from any starting node, find a node's parent, list all descendants, or fold over every node in the tree.
The Tree holds all the nodes and tracks the Integer ID to node mapping.
Create an empty tree with no nodes. tree = Tree.empty Tree.nodeCount tree == 0
Insert a node into the tree. Returns the auto-assigned id and the updated tree. The node starts with no children.
result = Tree.insert "root" Tree.empty
result.id == 0
Tree.nodeCount result.tree == 1
result2 = Tree.insert "child" result.tree
result2.id == 1
Another example:
let
r1 =
Tree.insert "root" Tree.empty
r2 =
Tree.insert "childA" r1.tree
r3 =
Tree.insert "childB" r2.tree
tree =
r3.tree
|> Tree.appendChild r1.id r2.id
|> Tree.appendChild r1.id r3.id
in
Tree.children r1.id tree
Remove a single node by id. Any other node that lists this id as a child will have it scrubbed from its children array. Does not remove the node's own children from the tree.
tree = Tree.empty
|> Tree.insert "a" |> .tree
|> Tree.insert "b" |> .tree
Tree.nodeCount tree == 2
trimmed = Tree.remove 0 tree
Tree.nodeCount trimmed == 1
Tree.member 0 trimmed == False
Remove a node and all of its descendants recursively. Also scrubs removed ids from any remaining children arrays.
-- Given a tree: root(0) -> child(1) -> grandchild(2)
trimmed = Tree.removeRecursive 1 tree
Tree.member 1 trimmed == False
Tree.member 2 trimmed == False
Tree.children 0 trimmed == []
Get the full entry for a node: its value and children ids.
result = Tree.insert "hello" Tree.empty
Tree.get result.id result.tree
== Just { value = "hello", children = [] }
Tree.get 999 Tree.empty == Nothing
Get just the value of a node, ignoring its children.
result = Tree.insert "hello" Tree.empty
Tree.getNode result.id result.tree == Just "hello"
Tree.getNode 999 Tree.empty == Nothing
Get the children ids of a node. Returns an empty array if the node does not exist.
-- Given a tree where node 0 has children [1, 2]:
Tree.children 0 tree == [1, 2]
Tree.children 999 tree == []
Get the values of a node's children, without ids. Returns an empty array if the node does not exist.
-- Given node 0 has children [1, 2]:
Tree.childrenNodes 0 tree == ["childA", "childB"]
Get the children of a node, with both id and value for each child. Returns an empty array if the node does not exist.
-- Given node 0 has children [1, 2]:
Tree.childrenWithNodes 0 tree
== [ { id = 1, node = "childA" }, { id = 2, node = "childB" } ]
Find the parent id of a node. Returns Nothing if the node has no parent (i.e. it is a root or does not exist).
-- Given: root(0) -> child(1) -> grandchild(2)
Tree.parent 1 tree == Just 0
Tree.parent 2 tree == Just 1
Tree.parent 0 tree == Nothing
Append a child id to the end of a parent's children array. If the parent does not exist, the tree is returned unchanged.
r1 = Tree.insert "parent" Tree.empty
r2 = Tree.insert "child" r1.tree
tree = Tree.appendChild r1.id r2.id r2.tree
Tree.children r1.id tree == [1]
Replace a node's entire children array. If the node does not exist, the tree is returned unchanged.
-- Given node 0 currently has children [1, 2, 3]:
tree2 = Tree.setChildren 0 [2] tree
Tree.children 0 tree2 == [2]
Update a node's value by applying a function to it. If the node does not exist, the tree is returned unchanged.
-- Given node 0 has value "hello":
tree2 = Tree.updateNode 0 String.toUpper tree
Tree.getNode 0 tree2 == Just "HELLO"
Replace a node's value entirely, preserving its children.
-- Given node 0 has value "hello":
tree2 = Tree.replaceNode 0 "goodbye" tree
Tree.getNode 0 tree2 == Just "goodbye"
Tree.children 0 tree2 == Tree.children 0 tree
Transform every node value in the tree. The tree structure (ids and children) is preserved.
-- Given a Tree Int with nodes 0 -> 10, 1 -> 20:
mapped = Tree.map (\n -> n * 2) tree
Tree.getNode 0 mapped == Just 20
Tree.getNode 1 mapped == Just 40
Fold over every node in the tree in Dict key order. The folding function receives the id, value, children array, and accumulator.
-- Collect all node values into an array:
values =
Tree.foldl
(\id value childIds acc -> Array.pushLast value acc)
[]
tree
Get the ids of all root nodes. A root is any node that is not listed as a child of any other node.
-- Given: root1(0) -> child(2), root2(1)
Tree.roots tree == [0, 1]
Get all descendant ids of a node, recursively. Does not include the node itself.
-- Given: root(0) -> a(1) -> b(2), root(0) -> c(3)
Tree.descendants 0 tree == [1, 2, 3]
Tree.descendants 1 tree == [2]
Tree.descendants 2 tree == []
Perform a depth-first traversal starting from a given node. Visits the node first, then recurses into each child in order. Returns an array of { id, value, depth } records where depth is relative to the starting node (which has depth 0).
-- Given: root(0,"r") -> a(1,"a") -> b(2,"b")
-- -> c(3,"c")
Tree.depthFirst 0 tree ==
[ { id = 0, value = "r", depth = 0 }
, { id = 1, value = "a", depth = 1 }
, { id = 2, value = "b", depth = 2 }
, { id = 3, value = "c", depth = 1 }
]
-- You can also start mid-tree:
Tree.depthFirst 1 tree ==
[ { id = 1, value = "a", depth = 0 }
, { id = 2, value = "b", depth = 1 }
]
Flatten the entire tree into an array of records.
-- Given two nodes in the tree:
Tree.toArray tree ==
[ { id = 0, value = "root", children = [1] }
, { id = 1, value = "child", children = [] }
]
Get all node ids in the tree.
-- Given three nodes with ids 0, 1, 2:
Tree.ids tree == [0, 1, 2]
Check whether a node id exists in the tree.
result = Tree.insert "hi" Tree.empty
Tree.isMember 0 result.tree == True
Tree.isMember 5 result.tree == False
Get the total number of nodes in the tree.
Tree.nodeCount Tree.empty == 0
tree = Tree.empty
|> Tree.insert "a" |> .tree
|> Tree.insert "b" |> .tree
Tree.nodeCount tree == 2
Return a hierarchical representation of the Tree for debugging purposes. It needs a helper function to convert a node to a String.