首页 > 解决方案 > 在地图中合并嵌套值的正确方法?

问题描述

鉴于以下情况:

M1 = #{ "Robert" => #{"Scott" => #{}} },
M2 = #{ "Robert" => #{"Adams" => #{}} }

合并应该是:

M3 = #{ "Robert" => #{ "Scott" => #{}, "Adams" => {}}

现在,如果我们合并以下内容:

M4 = #{ "William" => #{ "Robert" => #{ "Scott" => {} }}}
M5 = #{ "William" => #{ "Robert" => #{ "Fitzgerald" => {} }}}

我们应该得到以下信息:

M6 = #{ "Robert" => #{ "Scott" => #{}, "Adams" => {}, 
        "William" => #{ "Robert" => #{ "Fitzgerald" => {}, "Scott" => {} }}}

我有迭代的想法,获取每个级别的键并迭代它们。检查它们是否相同,如果没有则合并地图,检查是否是地图,如果它没有停止并合并,否则再次调用自身。我遇到的问题是功能不断崩溃,有没有更好的方法来做到这一点?

这是我到目前为止的代码:

merger(M1, M2) ->
   M1_Keys = maps:keys(M1),
   M2_Keys = maps:keys(M2),
   do_merge(M1, M2, M1_Keys).

do_merge(M1, M2, [Head|Tail]) ->
   Check = check_if_same(M1, M2),
   io:fwrite("Check is: ~p\n", [Check]),
   case Check of 
     {ok, true} -> 
       io:fwrite("true\n");
     {ok, false} ->
       io:fwrite("false\n")
   end,
   do_merge(M1, M2, Tail);
%   P1 = maps:get(Head, M1),
%   P2 = maps:get(Head, M2),
%   P3 = maps:merge(P1, P2),
%   M4 = maps:update(Head, P3, M1),
%   io:fwrite("~p \n", [M4]),
%   do_merge(M1, M2, Tail);

do_merge(M1, M2, []) -> 
   ok.
check_if_same(M1, M2) -> 
   {ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.

但是,它崩溃并出现以下错误:

$erlc *.erl
helloworld.erl:10: Warning: variable 'M2_Keys' is unused
helloworld.erl:13: Warning: variable 'Head' is unused
helloworld.erl:30: Warning: variable 'M1' is unused
helloworld.erl:30: Warning: variable 'M2' is unused
$erl -noshell -s helloworld start -s init stop
Check is: {ok,true}
true
{"init terminating in do_boot",{{badmap,ok},[{maps,keys,[ok],[]},{helloworld,merger,2,[{file,"helloworld.erl"},{line,10}]},{init,start_em,1,[]},{init,do_boot,3,[]}]}}
init terminating in do_boot ()

Crash dump is being written to: erl_crash.dump...done

标签: dictionaryrecursionerlangerlang-shellerl

解决方案


你的do_merge回报ok总是(基本递归案例)。

在这里你有两个解决方案,第一个更具可读性,但我会选择第二个

deep_map_merge(M1, M2) when is_map(M1), is_map(M2) ->
    % Merge both as if they had no common keys
    FlatMerge = maps:merge(M1, M2),
    % Get common keys (This is O(N^2), there are better ways)
    CommonKeys = [K || K <- maps:keys(M1), K2 <- maps:keys(M2), K == K2],
    % Update the merged map with the merge of the common keys
    lists:foldl(fun(K, MergeAcc) ->
                        MergeAcc#{K => deep_map_merge(maps:get(K, M1), maps:get(K, M2))}
                end, FlatMerge, CommonKeys);
deep_map_merge(_, Override) ->
    Override.


deep_map_merge2(M1, M2) when is_map(M1), is_map(M2) ->
    maps:fold(fun(K, V2, Acc) ->
                      case Acc of
                          #{K := V1} ->
                              Acc#{K => deep_map_merge2(V1, V2)};
                          _ ->
                              Acc#{K => V2}
                      end
              end, M1, M2);
deep_map_merge2(_, Override) ->
    Override.

推荐阅读