合并/合并两个Erlang列表

gkn4icbw  于 2022-12-08  发布在  Erlang
关注(0)|答案(6)|浏览(235)

How to combine tuple lists in erlang? I have lists:

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

and

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

now I want a combined list as :

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

wz1wpwve1#

有一个很好的解决方案,使用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.
ebdffaop

ebdffaop2#

一些更短的,列表甚至不必拥有相同的键,并且可以无序:

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可以直接写在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"是因为Erlang将[10,90]解释为字符串(实际上是列表)。

wmtdaxz3

wmtdaxz33#

这种技术称为合并连接,在数据库设计中是众所周知的。

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}.
64jmpszr

64jmpszr4#

lists:zipwith/2怎么了?
假设条件:

  • 列表长度相同
  • 列表包含相同顺序的相同键

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

6yoyoihd

6yoyoihd5#

也许这不是最好的方法,但它确实达到了你想要达到的目的。

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对其进行排序。

7gs2gvoe

7gs2gvoe6#

您可以使用列表解析:

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"}],
  [ {K,[V1,V2]} ||  {K,V1} <- L1, {K2,V2} <- L2, K == K2].

输出量:

[{k1,"\nZ"}, % [10,90] shown in console as string..
 {k2,[20,210]},
 {k3,[30,60]},
 {k4,[20.9,66.9]},
 {k6,["Hello world","Hello universe"]}]

相关问题