我有一个像这样的数据结构列表:
defmodule Foo do
defstruct [:id, :parent_id, :children]
end
lst = [
%Foo{id: 1, parent_id: nil, children: []},
%Foo{id: 2, parent_id: 1, children: []},
%Foo{id: 4, parent_id: 1, children: []},
%Foo{id: 3, parent_id: 2, children: []},
]
列表按parent_id
和id
排序,因此列表中较低的parent_id
比较低的id
更早。我想将该列表转换为分层数据结构:
%Foo{id: 1, parent_id: nil, children: [
%Foo{id: 2, parent_id: 1, children: [
%Foo{id: 3, parent_id: 2, children: []},
]},
%Foo{id: 4, parent_id: 1, children: []}
]}
我对递归循环和Enum.filter
有一个天真的想法,但这似乎效率很低。任何想法如何有效地解决这个问题?
修改:
我似乎有一个可行的解决方案,但效率也非常低:
defp build_tree(root, []), do: root
defp build_tree(root, [node | tail]) do
build_tree(insert_node(root, node), tail)
end
defp insert_node(root = %Message{}, node) do
if root.message_id == node.parent_id do
new_messages = case root.messages do
nil ->
[node]
_ ->
root.messages ++ [node]
end
%Message{root | messages: new_messages}
else
acc = case root.messages do
nil -> []
_ -> root.messages
end
%Message{root | messages: insert_node(acc, node)}
end
end
defp insert_node(root, node) do
Enum.map(root, fn(x) -> insert_node(x, node) end)
end
[first | rest] = sorted_messages
tree = build_tree(first, rest)
任何有关更好解决方案的想法?
答案 0 :(得分:7)
由于记录按parent_id
然后id
排序,因此这是一种相当有效的方法。它只涉及遍历列表两次:一次反向,一次减少。 reduce本身会执行一些Map操作,但它们仍然只有O(log n)
,所以整个事情都是O(n log n)
:
tree =
list
|> Enum.reverse
|> Enum.reduce(%{}, fn foo, map ->
foo = %{foo | children: Map.get(map, foo.id, [])}
Map.update(map, foo.parent_id, [foo], fn foos -> [foo | foos] end)
end)
|> Map.get(nil)
|> hd
我们的核心理念是保留一个由parent_id
Foo
键入的临时地图,每当我们看到当前Foo的id
出现在地图中时,我们就会看到它children
将其放入当前Foo
,然后将当前Foo
插入其children
的{{1}}。最后,我们只取出parent_id
Foo
并使用{。}}。
演示:
parent_id: nil
输出:
defmodule Foo do
defstruct [:id, :parent_id, :children]
end
defmodule Main do
def main do
list =
[%Foo{id: 1, parent_id: nil, children: []},
%Foo{id: 2, parent_id: 1, children: []},
%Foo{id: 4, parent_id: 1, children: []},
%Foo{id: 3, parent_id: 2, children: []}]
expected =
%Foo{id: 1, parent_id: nil, children: [
%Foo{id: 2, parent_id: 1, children: [
%Foo{id: 3, parent_id: 2, children: []},
]},
%Foo{id: 4, parent_id: 1, children: []}
]}
tree =
list
|> Enum.reverse
|> Enum.reduce(%{}, fn foo, map ->
foo = %{foo | children: Map.get(map, foo.id, [])}
Map.update(map, foo.parent_id, [foo], fn foos -> [foo | foos] end)
end)
|> Map.get(nil)
|> hd
IO.inspect tree
IO.inspect tree == expected
end
end
Main.main