我有一個列表我要大塊了基於的B結構類型爲A的過渡因此,例如,我有以下的分塊列表該數據分塊成含有2個元素的列表:基於結構類型改變
[ [ %A{}, %A{}, %B{}, %B{}, %B{} ], [ %A{}, %A{}, %B{} ] ]
如果輸入要成爲A的所有或所有B的最初,輸出將是不變的,因爲沒有B-> A轉換髮生。
我想Enum.chunk_by/2
是要走的路,但我無法弄清楚如何維護上一個元素的上下文以知道何時分割。
這是什麼樣的東西的習慣解決方案?
我有一個列表我要大塊了基於的B結構類型爲A的過渡因此,例如,我有以下的分塊列表該數據分塊成含有2個元素的列表:基於結構類型改變
[ [ %A{}, %A{}, %B{}, %B{}, %B{} ], [ %A{}, %A{}, %B{} ] ]
如果輸入要成爲A的所有或所有B的最初,輸出將是不變的,因爲沒有B-> A轉換髮生。
我想Enum.chunk_by/2
是要走的路,但我無法弄清楚如何維護上一個元素的上下文以知道何時分割。
這是什麼樣的東西的習慣解決方案?
另一種選擇是chunk_by結構類型然後執行另一次合併列表(當該列表包含%B{}
除外):
def chunk(structs) do
structs
|> Enum.chunk_by(& &1.__struct__)
|> merge()
end
# Don't merge when current is %B
defp merge([[%B{}|_]=h|t]), do: [h|merge(t)]
# Merge all others
defp merge([curr, next|t]), do: [curr ++ next|merge(t)]
# We are done
defp merge([]), do: []
Enum.chunk_by/2
當前不提供對前一個元素的訪問權限,因此在這種情況下我們不能使用Enum.chunk_by/2
。我們將不得不退回到reduce/3
所有Enum
功能中,reduce/3
是最靈活,在內部被使用最多的,如果不是全部的Enum
功能。
下面是去生產中給出的數值[ %A{}, %A{}, %B{}, %B{}, %B{}, %A{}, %A{}, %B{} ]
你想要的輸出,一個辦法:
values
|> Enum.reduce([[]], fn (elem, acc) ->
prev_list = List.first(acc)
prev_elem = List.first(prev_list)
b_changed_to_a? = fn -> prev_elem.__struct__ == B && elem.__struct__ == A end
if is_nil(prev_elem) || !b_changed_to_a?.() do
List.replace_at(acc, 0, [elem|prev_list])
else
[[elem]|acc]
end
end)
|> Enum.map(&Enum.reverse/1)
|> Enum.reverse
請注意,我總是在前面加上一個元素的列表。這是因爲在Elixir中添加列表是一項昂貴的操作。
希望這個解決方案有幫助!
另一種方法是使用純遞歸:
def collect_chunks([]), do: []
def collect_chunks(list) do
{chunk, post_chunk} = collect_chunk(list)
[chunk | collect_chunks(post_chunk)]
end
defp collect_chunk([]), do: {[], []}
defp collect_chunk([%B{} = last_element | [%A{} | _] = post_chunk]), do: {[last_element], post_chunk}
defp collect_chunk([el | rest]) do
{remaining_chunk, post_chunk} = collect_chunk(rest)
{[el | remaining_chunk], post_chunk}
end
必須選擇這一個,因爲根據我目前的Elixir知識水平,這是我唯一能夠維持的一個!謝謝! –