我想为列表中的每个相邻项目添加一个字段以包含彼此的id,即创建一个双向链表。原因是列表按特定的数据库查询排序,我想在某些前端模板中使用此顺序。
在过程语言中,使用 for 循环很简单,我可以在其中更改循环体中的myList[index]
和myList[index + 1]
字段。
我已经考虑了几种在Elixir中做到这一点的方法,例如chunk_every
,zip
列表本身移动了1,或者Enum.with_index
与Enum.at
一起移动,但到目前为止,没有一个是真正令人满意/有效的。
惯用的长生不老药方法是什么?
你可以递归通过常规链表并解构当前头和下一个头。要查找的特殊情况就在基本情况之前(即没有"next_id"或类似内容的项目。
例如,如果我们有一个地图列表,例如:
my_list = [
%{id: 1, next: nil, previous: nil},
%{id: 2, next: nil, previous: nil},
...
]
def doubly_link([]), do: []
def doubly_link([head | []]) do
head = %{head | next: nil}
[head | doubly_link([])]
end
def doubly_link([head | [next_head | tail]]) do
head = %{head | next: next_head.id}
next_head = %{next_head | previous: head.id}
[head | doubly_link([next_head | tail])]
end
然后调用doubly_link(my_list)
将产生:
[
%{id: 1, previous: nil, next: 2},
%{id: 2, previous: 1, next: 3},
....,
%{id: n, previous: prior_to_n_id, next: nil}
]
另一种方法是使用Enum.with_index/2
两次:
input = Enum.map(0..4, & %{id: &1})
head = -1
tail = Enum.count(input)
input
|> Enum.with_index(head)
|> Enum.with_index(head + 2)
|> Enum.map(fn
{{value, ^head}, next} ->
Map.put(value, :next, next)
{{value, prev}, ^tail} ->
Map.put(value, :prev, prev)
{{value, prev}, next} ->
value
|> Map.put(:prev, prev)
|> Map.put(:next, next)
end)
#⇒ [
# %{id: 0, next: 1},
# %{id: 1, next: 2, prev: 0},
# %{id: 2, next: 3, prev: 1},
# %{id: 3, next: 4, prev: 2},
# %{id: 4, prev: 3}
# ]