Skip to content

Commit 82c5ff3

Browse files
authored
feat: Add map:zipfold (#37)
1 parent b08ef4d commit 82c5ff3

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

src/genlib_map.erl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
-export([diff/2]).
2020
-export([fold_while/3]).
2121
-export([search/2]).
22+
-export([zipfold/4]).
2223

2324
%%
2425

@@ -176,3 +177,25 @@ do_search(Fun, {Key, Value, Iter}) ->
176177
true -> {Key, Value};
177178
false -> do_search(Fun, maps:next(Iter))
178179
end.
180+
181+
%% @doc Fold two maps "joining" them by key.
182+
%% NOTE: If a key-value exists only in one map, this pair is ignored altogether
183+
-spec zipfold(
184+
fun((K, V1, V2, A) -> A),
185+
InitAcc :: A,
186+
#{K => V1},
187+
#{K => V2}
188+
) -> A.
189+
zipfold(Fun, Acc, M1, M2) ->
190+
maps:fold(
191+
fun(Key, V1, AccIn) ->
192+
case maps:find(Key, M2) of
193+
{ok, V2} ->
194+
Fun(Key, V1, V2, AccIn);
195+
error ->
196+
AccIn
197+
end
198+
end,
199+
Acc,
200+
M1
201+
).

test/prop_genlib_map.erl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,63 @@ prop_search_not_found() ->
5757
end
5858
).
5959

60+
-spec prop_zipfold() -> proper:test().
61+
prop_zipfold() ->
62+
?FORALL(
63+
[Left, Right],
64+
[map(), map()],
65+
begin
66+
CommonKeys = maps_common_keys(Left, Right),
67+
68+
Actual =
69+
genlib_map:zipfold(
70+
fun(Key, LValue, RValue, Acc) -> Acc#{Key => {LValue, RValue}} end,
71+
#{},
72+
Left,
73+
Right
74+
),
75+
76+
Expected =
77+
maps_merge_with(
78+
fun(_Key, LValue, RValue) -> {LValue, RValue} end,
79+
Left,
80+
Right
81+
),
82+
83+
Actual =:= maps:with(CommonKeys, Expected)
84+
end
85+
).
86+
87+
-if(?OTP_RELEASE >= 24).
88+
89+
maps_merge_with(Combiner, Left, Right) ->
90+
maps:merge_with(Combiner, Left, Right).
91+
92+
-else.
93+
94+
maps_merge_with(Combiner, Left, Right) ->
95+
CommonMerged =
96+
lists:foldl(
97+
fun(Key, Acc) ->
98+
Acc#{Key => Combiner(Key, maps:get(Key, Left), maps:get(Key, Right))}
99+
end,
100+
#{},
101+
maps_common_keys(Left, Right)
102+
),
103+
104+
maps:merge(maps:merge(Left, Right), CommonMerged).
105+
106+
%% END -if(?OTP_RELEASE >= 24).
107+
-endif.
108+
109+
maps_common_keys(LeftMap, RightMap) ->
110+
sets:to_list(
111+
sets:intersection(
112+
sets:from_list(maps:keys(LeftMap)),
113+
sets:from_list(maps:keys(RightMap))
114+
)
115+
).
116+
60117
map() ->
61118
?LET(KVList, list({term(), term()}), maps:from_list(KVList)).
62119

0 commit comments

Comments
 (0)