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}
]
}