How to implement a multiple tree?

Here is another approach, using a nested map of maps as tree representation.

I have documented the code to make it hopefully easy to understand. :slight_smile:


defmodule DepthFirstTree do
  def main(list \\ ["/2/3", "/2/4", "/2/4/6", "/2/3/7", "/2/3/5", "/2/3/8"]) do
    list
    |> build_tree
    |> traverse_depth_first
  end

  @doc """
  Transforms a list of paths into a nested map of maps, by splitting the paths on `/`.
  """
  def build_tree(paths) do
    paths
    |> Enum.map(&path_to_tree/1)
    |> Enum.reduce(%{}, &deep_merge/2)
  end

  @doc """
  Turns a path like "a/b/c" into a nested map of maps like %{"a" => %{"b" => %{"c" => %{}}}}
  """
  def path_to_tree(path) do
    path
    |> String.split("/")
    |> Enum.reverse
    |> Enum.reduce(%{}, fn segment, inner_tree -> %{segment => inner_tree} end)
  end

  @doc """
  Combines two nested map of maps.
  """
  def deep_merge(map1, map2) do
    Map.merge(map1, map2, fn _, val1 = %{}, val2 = %{} ->
      deep_merge(val1, val2)
    end)
  end

  @doc """
  Does a depth-first traversal over a nested map of maps, in sorted-key order.
  First visits the current node, then its lowest child subtree, then the second-lowest subtree, ... then the highest subtree.

  This implementation is not tail-recursive; more performant alternatives (that might be less readable) probably exist.
  """
  def traverse_depth_first(prefix \\ nil, tree) do
    tree
    |> Enum.sort
    |> Enum.flat_map(fn
      {key, subtree} ->
        full_prefix = reconstruct_path(prefix, key)
        [full_prefix | traverse_depth_first(full_prefix, subtree)]
    end)
  end

  @doc """
  Helper function to combine a prefix and a key back to a path.
  """
  def reconstruct_path(prefix, key) do
    if prefix == nil do
      key
    else
      prefix <> "/" <> key
    end
  end
end