【问题标题】:Combine/Merge Two Erlang lists合并/合并两个 Erlang 列表
【发布时间】:2014-03-19 09:06:32
【问题描述】:

如何在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"]}
     ].

【问题讨论】:

    标签: erlang


    【解决方案1】:

    您可以使用列表推导:

      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"]}]
    

    【讨论】:

      【解决方案2】:

      使用 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.
      

      【讨论】:

      • 这似乎很有趣!谢谢。在这里,字符串顺序并不是那么重要。这很棒!谢谢
      • 确实不错,我从来没有想过使用高级集合操作,也许是因为我很喜欢数学;)
      【解决方案3】:

      lists:zipwith/2怎么了?

      假设:

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

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

      【讨论】:

      • 很酷很漂亮,在一条线上。谢谢
      【解决方案4】:

      也许这不是最好的方法,但它可以达到你想要达到的效果。

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

      【讨论】:

      • 谢谢,我会试试这个建议!
      • 我得到了结果,但不确定为什么我在第一个元组中得到 \nZ:10&gt; a:combine_lists(). [{k1,"\nZ"}, {k2,[20,210]}, {k3,[30,60]}, {k4,[20.9,66.9]}, {k6,["Hello world","Hello universe"]}]. Pl.帮助。谢谢
      • 我假设输入列表的顺序与您的示例输入中一样。 @Carlos:哦,抱歉,它们没有排序
      • 嗯,你可以使用lists:keysort/2
      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

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

        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] 解释为字符串(实际上是列表)。不要打扰。

        【讨论】:

        • 感谢@carlo 和 Berzemus 的建议
        猜你喜欢
        • 2012-02-23
        • 2021-11-11
        • 1970-01-01
        • 2016-01-17
        • 2017-03-23
        • 2014-03-19
        • 2014-12-18
        • 2016-01-12
        相关资源
        最近更新 更多