2014-02-19 27 views
5

Wie kombiniere ich Tupel-Listen in Erlang? Ich habe Listen:Kombinieren/Zusammenführen von zwei Erlang-Listen

L1 = [{k1, 10}, {k2, 20}, {k3, 30}, {k4, 20.9}, {k6, "Hello world"}], 

und

L2 = [{k1, 90}, {k2, 210}, {k3, 60}, {k4, 66.9}, {k6, "Hello universe"}], 

jetzt will ich eine kombinierte Liste als:

L3 = [ 
     {k1, [10, 90]}, 
     {k2, [20, 210]}, 
     {K3, [30, 60]}, 
     {k4, [20.9, 66.9]}, 
     {K6, ["Hello world", "Hello universe"]} 
    ]. 

Antwort

4

Etwas kürzer, und die Listen müssen nicht einmal die gleichen Schlüssel besitzen, und können ungeordnete sein:

merge(In1,In2) -> 
    Combined = In1 ++ In2, 
    Fun  = fun(Key) -> {Key,proplists:get_all_values(Key,Combined)} end, 
    lists:map(Fun,proplists:get_keys(Combined)). 

Fun direkt in der Funktion lists:map/2 geschrieben werden kann, aber das macht es lesbar.

Ausgang, mit den Daten aus Beispiel:

1> test:merge(L1,L2). 
[{k1,"\nZ"}, 
{k2,[20,210]}, 
{k3,[30,60]}, 
{k4,[20.9,66.9]}, 
{k6,["Hello world","Hello universe"]}] 

"\nZ" ist, weil erlang interpretiert [10,90] als String (die in der Tat Listen). Mach dir keine Sorgen.

+0

Vielen Dank @carlo und Berzemus für Vorschläge –

2

Vielleicht ist dies nicht der beste Weg ist, aber es tut, was Sie versuchen, leisten.

merge([{A, X}| T1], [{A, Y} | T2], Acc) -> 
    New_acc = [{A, [X, Y]} | Acc], 
    merge(T1, T2, New_acc); 

merge([{A, X} | T1], [{B, Y} | T2], Acc) -> 
    New_acc = [{A, [X]}, {B, Y} | Acc], 
    merge(T1, T2, New_acc); 

merge([], [{B, Y} | T], Acc) -> 
    New_acc = [{B, Y} | Acc], 
    merge([], T, New_acc); 

merge([{A, X} | T], [], Acc) -> 
    New_acc = [{A, X} | Acc], 
    merge(T, [], New_acc); 

merge([], [], Acc) -> 
    lists:reverse(Acc). 

bearbeiten Ich gehe davon aus, dass die Eingabelisten wie in Ihrem Abtastwerteingang bestellt werden. Wenn nicht, können Sie lists:sort/2 verwenden, um sie vor dem Zusammenführen zu sortieren.

+0

Vielen Dank, ich werde diesen Vorschlag versuchen! –

+0

Ich habe das Ergebnis, aber nicht sicher, warum ich \ NZ im ersten Tupel bekomme: '10> a: combine_lists(). [{k1, "\ nZ"}, { k2, [20210]}, { k3, [30,60]}, { k4, [20.9,66.9]}, { k6, [ "Hallo Welt "," Hallo Universum "]}].' Pl. Hilfe. Danke –

+0

_Ich nehme an, dass die Eingabelisten wie in deiner Beispieleingabe angeordnet sind. _ @Carlos: Ohh dann Sorry, sie sind nicht bestellt –

4

Diese Technik wird Merge Join genannt. Es ist in der Datenbank bekannt.

merge(L1, L2) -> 
    merge_(lists:sort(L1), lists:sort(L2)). 

merge_([{K, V1}|T1], [{K, V2}|T2]) -> [{K, [V1, V2]}|merge_(T1, T2)]; 
merge_([], []) -> []. 

Wenn es verschiedene Sätze von Tasten in beiden Listen sein können und Sie bereit sind, diese Werte fallen können Sie

merge_([{K, V1}|T1], [{K, V2}|T2]) -> [{K, [V1, V2]}|merge_(T1, T2)]; 
merge_([{K1, _}|T1], [{K2, _}|_]=L2) when K1 < K2 -> merge_(T1, L2); 
merge_(L1, [{_, _}|T2]) -> merge_(L1, T2);` 
merge_(_, []) -> []. 

oder wenn Sie speichern möchten diese Werte in Listen

merge_([{K, V1}|T1], [{K, V2}|T2]) -> [{K, [V1, V2]}|merge_(T1, T2)]; 
merge_([{K1, V1}|T1], [{K2, _}|_]=L2) when K1 < K2 -> [{K1, [V1]}|merge_(T1, L2)]; 
merge_(L1, [{K2, V2}|T2]) -> [{K2, [V2]}|merge_(L1, T2)]; 
merge_(L1, []) -> [{K, [V]} || {K, V} <- L1]. 

Sie können natürlich tail rekursive Version verwenden, wenn Sie nichts dagegen haben, in umgekehrter Reihenfolge oder Sie können immer lists:reverse/1

verwenden
merge(L1, L2) -> 
    merge(lists:sort(L1), lists:sort(L2), []). 

merge([{K, V1}|T1], [{K, V2}|T2], Acc) -> merge(T1, T2, [{K, [V1, V2]}|Acc]); 
merge([], [], Acc) -> Acc. % or lists:reverse(Acc). 

Oder

merge([{K, V1}|T1], [{K, V2}|T2], Acc) -> merge(T1, T2, [{K, [V1, V2]}|Acc]); 
merge([{K1, _}|T1], [{K2, _}|_]=L2, Acc) when K1 < K2 -> merge(T1, L2, Acc); 
merge(L1, [{_, _}|T2], Acc) -> merge(L1, T2, Acc);` 
merge(_, [], Acc) -> Acc. % or lists:reverse(Acc). 

Oder

merge([{K, V1}|T1], [{K, V2}|T2], Acc) -> merge(T1, T2, [{K, [V1, V2]}|Acc]); 
merge([{K1, V1}|T1], [{K2, _}|_]=L2, Acc) when K1 < K2 -> merge(T1, L2, [{K1, [V1]}|Acc]); 
merge(L1, [{K2, V2}|T2], Acc) -> merge(L1, T2, [{K2, [V2]}|Acc]);` 
merge([{K1, V1}|T1], [], Acc) -> merge(T1, [], [{K1, [V1]} | Acc]); 
merge([], [], Acc) -> Acc. % or lists:reverse(Acc). 
% or merge(L1, [], Acc) -> lists:reverse(Acc, [{K, [V]} || {K, V} <- L1]). 
% instead of two last clauses. 

Wenn es möglich ist, dass eine der Listen gleichen Schlüssel enthalten können, und Sie sind alle Werte bereit collect können Sie diese

merge(L1, L2) -> 
    merge(lists:sort(L1), lists:sort(L2), []). 

merge([{K1, _}|_]=L1, {K2, _}|_]=L2, Acc) -> 
    K = min(K1, K2), 
    {Vs1, T1} = collect(K, L1, []), 
    {Vs2, T2} = collect(K, L2, Vs1), 
    merge(T1, T2, [{K, Vs2}|Acc]); 
merge([{K, _}|_]=L1, [], Acc) -> 
    {Vs, T1} = collect(K, L1, []), 
    merge(T1, [], [{K, Vs}|Acc]); 
merge([], [{K, _}|_]=L2, Acc) -> 
    {Vs, T2} = collect(K, L2, []), 
    merge([], T2, [{K, Vs}|Acc]); 
merge([], [], Acc) -> lists:reverse(Acc). 

collect(K, [{K, V}|T], Acc) -> collect(K, T, [V|Acc]); 
collect(_, T, Acc) -> {Acc, T}. 
3
betrachten

Was ist mit lists:zipwith/2 passiert?

Annahmen:

  • Listen sind gleich lang
  • Listen die gleichen Schlüssel in der gleichen Reihenfolge

lists:zipwith(fun({X, Y}, {X, Z}) -> {X, [Y, Z]} end, L1, L2).

+0

Sehr cool und hübsch, in einer Linie. Danke –

5

Es gibt eine schöne Lösung für dieses ist durch die Verwendung enthalten das sofs Modul in der Erlang Standard-Bibliothek. Das Modul sofs beschreibt ein DSL zum Arbeiten mit mathematischen Sätzen. Dies ist eine dieser Situationen, in der du sie nutzen kannst, indem du deine Daten in die SOFS-Welt transformierst, sie innerhalb dieser Welt manipulierst und sie danach wieder nach außen transformierst.

Beachten Sie, dass ich Ihr L3 ein bisschen geändert habe, da sofs die String-Reihenfolge nicht beibehalten.

-module(z). 

-compile(export_all). % Don't do this normally :) 

x() -> 
    L1 = [{k1, 10}, {k2, 20}, {k3, 30}, {k4, 20.9}, {k6, "Hello world"}], 
    L2 = [{k1, 90}, {k2, 210}, {k3, 60}, {k4, 66.9}, {k6, "Hello universe"}], 
    L3 = [{k1, [10, 90]},{k2, [20, 210]},{k3, [30, 60]},{k4, [20.9, 66.9]},{k6, ["Hello universe", "Hello world"]}], 
    R = sofs:relation(L1 ++ L2), 
    F = sofs:relation_to_family(R), 
    L3 = sofs:to_external(F), 
    ok. 
+0

Das scheint ziemlich interessant zu sein! Vielen Dank. Hier ist String Order nicht so wichtig. Es ist großartig! Danke –

+0

Das ist in der Tat schön, es kam mir nie in den Sinn, erweiterte Set-Operationen zu verwenden, vielleicht weil ich sehr mathematisch geneigt bin;) – Berzemus

+0

@IGIVECRAPANSWERS - war nicht vertraut mit SOFs! coole Sache. – trex