2014-02-19 194 views
5

如何結合erlang中的元組列表?我有列表:合併/合併兩個Erlang列表

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 world", "Hello universe"]} 
    ]. 

回答

4

東西短,名單甚至沒有到有擁有的相同的密鑰,並且可以無序:

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

樂趣可以直接在lists:map/2功能寫入,但是這使得它的可讀性。

輸出,與來自實施例的數據:

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

"\nZ"是因爲二郎解釋[10,90]作爲字符串(它們是,事實上,列表)。不要打擾。

+0

謝謝@carlo和Berzemus的建議 –

2

也許這是不是最好的方式,但它做什麼,你想實現。

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). 

編輯 我假設輸入列表排序爲您的樣品輸入。如果不是,您可以在合併之前使用lists:sort/2對它們進行排序。

+0

謝謝,我會試試這個建議! –

+0

我得到了結果,但不知道爲什麼我在第一個元組中獲得\ nZ: '10> a:combine_lists()。 {k1,「\ nZ」}, {k2,[20,210]}, {k3,[30,60]}, {k4,[20.9,66.9]}, {k6,[「 「,」你好宇宙「]}]' Pl。幫幫我。謝謝 –

+0

_I'm假設輸入列表按照您的示例輸入排序._ @Carlos:哦,然後對不起,他們沒有訂購 –

4

這種技術被稱爲合併連接。它在數據庫設計中是衆所周知的。

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

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

如果可以有不同的套在兩個列表按鍵,你願意放棄這些價值,你可以使用

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_(_, []) -> []. 

或者,如果你想儲存在名單上的這些值

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]. 

你當然可以使用尾遞歸版本,如果你不介意結果相反,或者你總是可以使用lists:reverse/1

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). 

或者

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). 

或者

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. 

如果有可能,列出可以包含相同的密鑰願意和你收集的所有值,你可以考慮一下這款

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

lists:zipwith/2怎麼回事?

假設:

  • 列表是相同的長度
  • 列表包含以相同的順序

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

+0

非常酷和漂亮,在一行。謝謝 –

5

相同的鍵有一個很好的解決方案,這一個通過使用Erlang標準庫中的sofs模塊。 sofs模塊描述了使用數學集合的DSL。這是其中的一種情況,您可以通過將數據轉換爲SOFS世界來利用它,在該世界內操縱它們,然後在外面再將它們轉換回來。

請注意,我確實改變了你的L3,因爲sofs不保留字符串順序。

-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

這似乎很有趣!謝謝。這裏String Order並不那麼重要。這很棒!謝謝 –

+0

這真的很不錯,我從來沒有想過使用高級設置操作,也許是因爲我注意到非常數學上的傾向;) – Berzemus

+0

@IGIVECRAPANSWERS - 對sofs不熟悉!很酷的東西。 – trex