2015-05-16 10 views
8

Ich habe eine Liste, die ich Brocken bis zu wollen, basierend auf einem Übergang vom Strukturtyp B zu A. So zum Beispiel, ich habe folgende:Chunking Liste basiert auf Strukturtyp Ändern

iex(1)> defmodule A, do: defstruct [] 
{:module, A ... 
iex(2)> defmodule B, do: defstruct [] 
{:module, B ... 
iex(3)> values = [ %A{}, %A{}, %B{}, %B{}, %B{}, %A{}, %A{}, %B{} ] 
[%A{}, %A{}, %B{}, %B{}, %B{}, %A{}, %A{}, %B{}] 

Ich mag haben dass Daten chunked bis in eine 2-Element-Liste enthält:

[ [ %A{}, %A{}, %B{}, %B{}, %B{} ], [ %A{}, %A{}, %B{} ] ] 

Wenn der Eingang A des alle oder alle B zunächst sein, so würde der Ausgang unverändert bleiben, da kein B-> trat ein Übergang.

Ich stelle mir vor Enum.chunk_by/2 ist der Weg zu gehen, aber ich habe Probleme herauszufinden, wie man den Kontext des vorherigen Elements zu wissen, wann zu teilen.

Wie sieht eine idiomatische Lösung für so etwas aus?

Antwort

4

Eine weitere Alternative ist es, die Strukturtyp Sie dann einen weiteren Durchgang der Zusammenführung der Listen chunk_by (außer, wenn die Liste enthält %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: [] 
+0

Ich muss dieses hier auswählen, da es das einzige ist, das ich in der Lage bin zu halten, wenn ich mein aktuelles Wissen über Elixier anwende! Vielen Dank! –

4

Enum.chunk_by/2 bietet derzeit keinen Zugriff auf das vorherige Element, so dass wir in diesem Fall Enum.chunk_by/2 nicht verwenden können. Wir werden reduce/3

Von allen Enum Funktionen zum Rückfall haben, reduce/3 die flexibelste und wird intern von den meisten, wenn nicht alle der Enum Funktionen verwendet.

Im Folgenden finden Sie eine Möglichkeit, über die Herstellung der Ausgangs Sie gehen wollen, da die Werte [ %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 

Beachten Sie, dass ich immer ein Element zu einer Liste voranstellen. Dies liegt daran, dass das Hinzufügen zu einer Liste in Elixir eine teure Operation ist.

Hoffe, diese Lösung hilft!

5

Ein weiterer Ansatz ist rein Rekursion zu verwenden:

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