defmodule TreeMap do
@moduledoc """
A module to maintain a tree of the structure
```elixir
%Node{ key: ..., value: ..., children: [%TreeMap{},...]}
```
and functions to traverse such a tree.
### Examples
```elixir
root =
TreeMap.new("1", "Root")
|> TreeMap.add_child("1.1", "Sub One", [TreeMap.new("1.1.1", "SomeSubSub")])
|> TreeMap.add_child("1.2", "Sub Two", [TreeMap.new("1.2.1", "Some other SubSub")])
expected = ["1", "1.1", "1.1.1", "1.2", "1.2.1"]
received =
TreeMap.transduce(root, [], fn a, x -> a ++ [x] end, fn n, acc -> [n | acc] end)
|> Enum.map(& &1.key)
assert expected == received
```
"""
alias TreeMap.{Node, RootServer}
@doc """
### Examples
iex> TreeMap.new()
%TreeMap.Node{}
iex> TreeMap.new(123, "hundretandtwentythree")
%TreeMap.Node{ key: 123, value: "hundretandtwentythree"}
"""
def new(key \\ nil, value \\ nil, children \\ []) do
%Node{key: key, value: value, children: children}
end
@doc """
Return the children of this node.
"""
def children(tree_map), do: tree_map.children
@doc """
Prepend a new child to the children of this node.
### Example:
iex> TreeMap.new("1", "Root") |> TreeMap.add_child("1.1", "Sub One")
%TreeMap.Node{key: "1", value: "Root", children: [%TreeMap.Node{key: "1.1", value: "Sub One"}]}
"""
def add_child(tree_map, key, value, children \\ []) do
%{tree_map | children: [new(key, value, children) | tree_map.children]}
end
@doc """
Prepend an exisitng child to the children of this node.
### Example:
iex> child = TreeMap.new("0", "I exist")
iex> TreeMap.new("1", "Root") |> TreeMap.add_child(child)
%TreeMap.Node{key: "1", value: "Root", children: [%TreeMap.Node{key: "0", value: "I exist"}]}
"""
def add_child(tree_map, child) do
%{tree_map | children: [child | tree_map.children]}
end
@doc """
Traverse the tree map and apply the given function to each node.
### Example:
iex> root = TreeMap.new("1", "Root") |> TreeMap.add_child("1.1", "Sub One") |> TreeMap.add_child("1.2", "Sub Two")
...> TreeMap.traverse(root, fn x -> x.key end)
["1", ["1.1", [], "1.2", []]]
"""
def traverse(tree_map, fun) do
[
fun.(tree_map)
| [
Enum.flat_map(Enum.reverse(tree_map.children), fn child ->
traverse(child, fun)
end)
]
]
end
@doc """
Traverse the tree map and apply the given function to each node
and a reducer-function to it's children.
### Example:
iex> root = TreeMap.new("1", "Root")
...> |> TreeMap.add_child("1.1", "Sub One", [TreeMap.new("1.1.1", "SomeSubSub")])
...> |> TreeMap.add_child("1.2", "Sub Two", [TreeMap.new("1.2.1", "Some other SubSub")])
...> TreeMap.transduce(root, 38, fn a, x -> a + Enum.count(x.children) end, fn x, acc -> acc + x end)
42
iex> root = TreeMap.new("1", "Root")
...> |> TreeMap.add_child("1.1", "Sub One", [TreeMap.new("1.1.1", "SomeSubSub")])
...> |> TreeMap.add_child("1.2", "Sub Two", [TreeMap.new("1.2.1", "Some other SubSub")])
...> TreeMap.transduce(root, [], fn a, x -> a ++ [x.value] end, fn n, acc -> [n | acc] end)
["Root", "Sub One", "SomeSubSub", "Sub Two", "Some other SubSub"]
iex> root = TreeMap.new("1", "Root")
...> |> TreeMap.add_child("1.1", "Sub One", [TreeMap.new("1.1.1", "SomeSubSub")])
...> |> TreeMap.add_child("1.2", "Sub Two", [TreeMap.new("1.2.1", "Some other SubSub")])
...> TreeMap.transduce(root, [], fn a, x -> a ++ [x] end, fn n, acc -> [n | acc] end)
...> |> Enum.map(& &1.key)
["1", "1.1", "1.1.1", "1.2", "1.2.1"]
"""
def transduce(tree_map, acc, modifier, reducer) do
Enum.reduce(Enum.reverse(tree_map.children), modifier.(acc, tree_map), fn n, a ->
transduce(n, a, modifier, reducer)
end)
end
@doc """
Find a node by it's key or returns `false` in a given tree.
"""
def find(tree_map, key) do
transduce(tree_map, nil, fn a, n -> a || (n.key == key && n) end, fn n, acc ->
(n.key == key && n) || acc
end)
end
@doc """
Find a node by it's key or returns `false` in all root nodes.
"""
def find(key) do
list_roots()
|> Enum.reduce(false, fn node, found ->
found || find(node, key)
end)
end
@doc """
Create a Node and start a RootServer for it.
"""
def start_root_node(key, value) do
node = new(key, value)
RootServer.start_child(node)
end
@doc """
Start a root_node for the given `%Node{}`.
"""
def start_root_node(node) do
RootServer.start_child(node)
end
@doc """
Retreive the value of a given root node.
"""
def value(root_pid) do
Node.value(root_pid)
end
@doc """
List all root nodes from all RootServers.
"""
def list_roots() do
RootServer.list_roots()
end
@doc """
Kill all RootServers.
"""
def drop_all() do
RootServer.drop_all()
end
end