I would start by selecting root nodes, where parent_id is nil. Then use a recursive constructor to build children’s field.
iex> list = [
%{id: 2, name: "Child 2", parent_id: nil},
%{id: 1, name: "Child 1", parent_id: nil},
%{id: 3, name: "GrandChild 1", parent_id: 1},
%{id: 5, name: "Child 3", parent_id: nil},
%{id: 6, name: "GrandGrandChild 1", parent_id: 3}
]
iex> new_node = fn node -> %{id: node.id, name: node.name, children: Enum.filter(list, & &1.parent_id == node.id) |> Enum.map(& new_node.(&1))} end
#Function<44.65746770/1 in :erl_eval.expr/5>
iex> Enum.filter(list, & is_nil(&1.parent_id)) |> Enum.map(& new_node.(&1))
[
%{children: [], id: 2, name: "Child 2"},
%{
children: [
%{
children: [%{children: [], id: 6, name: "GrandGrandChild 1"}],
id: 3,
name: "GrandChild 1"
}
],
id: 1,
name: "Child 1"
},
%{children: [], id: 5, name: "Child 3"}
]






















