Skip to content

Commit

Permalink
Merge pull request #938 from pguyot/w45/implement-map-foreach
Browse files Browse the repository at this point in the history
Implement `maps:foreach/2`
  • Loading branch information
fadushin authored Nov 12, 2023
2 parents f11bcf4 + 0929d82 commit a344242
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
35 changes: 35 additions & 0 deletions libs/estdlib/src/maps.erl
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
find/2,
filter/2,
fold/3,
foreach/2,
map/2,
merge/2,
remove/2,
Expand Down Expand Up @@ -335,6 +336,33 @@ fold(_Fun, _Init, Map) when not is_map(Map) ->
fold(_Fun, _Init, _Map) ->
error(badarg).

%%-----------------------------------------------------------------------------
%% @param Fun function to call with every key-value pair
%% @param MapOrIterator the map or map iterator over which to iterate
%% @returns `ok'
%% @doc Iterate over the entries in a map.
%%
%% This function takes a function used to iterate over all entries in a map.
%%
%% This function raises a `badmap' error if `Map' is not a map or map iterator,
%% and a `badarg' error if the input function is not a function.
%% @end
%%-----------------------------------------------------------------------------
-spec foreach(
Fun :: fun((Key :: key(), Value :: value()) -> any()),
MapOrIterator :: map_or_iterator()
) -> ok.
foreach(Fun, Map) when is_function(Fun, 2) andalso is_map(Map) ->
iterate_foreach(Fun, maps:next(maps:iterator(Map)));
foreach(Fun, [Pos | Map] = Iterator) when
is_function(Fun, 2) andalso is_integer(Pos) andalso is_map(Map)
->
iterate_foreach(Fun, maps:next(Iterator));
foreach(_Fun, Map) when not is_map(Map) ->
error({badmap, Map});
foreach(_Fun, _Map) ->
error(badarg).

%%-----------------------------------------------------------------------------
%% @param Fun the function to apply to every entry in the map
%% @param Map the map to which to apply the map function
Expand Down Expand Up @@ -463,6 +491,13 @@ iterate_fold(Fun, {Key, Value, Iterator}, Accum) ->
NewAccum = Fun(Key, Value, Accum),
iterate_fold(Fun, maps:next(Iterator), NewAccum).

%% @private
iterate_foreach(_Fun, none) ->
ok;
iterate_foreach(Fun, {Key, Value, Iterator}) ->
_ = Fun(Key, Value),
iterate_foreach(Fun, maps:next(Iterator)).

%% @private
iterate_map(_Fun, none, Accum) ->
Accum;
Expand Down
37 changes: 37 additions & 0 deletions tests/libs/estdlib/test_maps.erl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ test() ->
ok = test_find(),
ok = test_filter(),
ok = test_fold(),
ok = test_foreach(),
ok = test_map(),
ok = test_merge(),
ok = test_remove(),
Expand Down Expand Up @@ -143,6 +144,42 @@ test_fold() ->
?ASSERT_ERROR(maps:fold(not_a_function, any, maps:new()), badarg),
ok.

collect_foreach(Acc) ->
Self = self(),
receive
{Self, Key, Value} ->
collect_foreach([{Key, Value} | Acc])
after 0 -> Acc
end.

test_foreach() ->
% maps:foreach/2 was introduced with OTP 24.
HasForeach =
case erlang:system_info(machine) of
"BEAM" -> erlang:function_exported(maps, foreach, 2);
"ATOM" -> true
end,
if
HasForeach ->
Self = self(),
Fun = fun(Key, Value) -> Self ! {self(), Key, Value} end,
ok = maps:foreach(Fun, maps:new()),
?ASSERT_EQUALS(collect_foreach([]), []),
ok =
receive
{Self, _, _} -> fail
after 0 -> ok
end,
ok = maps:foreach(Fun, #{a => 1, b => 2, c => 3}),
?ASSERT_EQUALS(lists:sort(collect_foreach([])), [{a, 1}, {b, 2}, {c, 3}]),
ok = check_bad_map(fun() -> maps:foreach(Fun, id(not_a_map)) end),
ok = check_bad_map_or_badarg(fun() -> maps:foreach(not_a_function, id(not_a_map)) end),
?ASSERT_ERROR(maps:foreach(not_a_function, maps:new()), badarg),
ok;
true ->
ok
end.

test_map() ->
Fun = fun(_Key, Value) -> 2 * Value end,
?ASSERT_EQUALS(maps:map(Fun, maps:new()), #{}),
Expand Down

0 comments on commit a344242

Please sign in to comment.