Build a tree from a flat structure recursively

Enum.group_by is a little more efficient than repeatedly scanning the whole list with Enum.filter, and (IMO) is easier to understand:

defmodule GroupThings do
  def run(data) do
    groups = Enum.group_by(data, & &1.parent_id)

    Enum.map(groups[nil], &associate_children(&1, groups))
  end

  defp associate_children(node, groups) do
    children =
      Enum.map(groups[node.id] || [], &associate_children(&1, groups))

    Map.put(node, :children, children)
  end
end

The key here is transforming the input into a map that can answer the question "what are the nodes that have a given parent_id":

%{
  1 => [%{id: 3, name: "GrandChild 1", parent_id: 1}],
  3 => [%{id: 6, name: "GrandGrandChild 1", parent_id: 3}],
  nil => [
    %{id: 2, name: "Child 2", parent_id: nil},
    %{id: 1, name: "Child 1", parent_id: nil},
    %{id: 5, name: "Child 3", parent_id: nil}
  ]
}
2 Likes