2014-02-19 223 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