Skip to content

Commit ef79250

Browse files
committed
Implement maps:foreach/2
Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent 9414701 commit ef79250

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

libs/estdlib/src/maps.erl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
find/2,
5151
filter/2,
5252
fold/3,
53+
foreach/2,
5354
map/2,
5455
merge/2,
5556
remove/2,
@@ -335,6 +336,33 @@ fold(_Fun, _Init, Map) when not is_map(Map) ->
335336
fold(_Fun, _Init, _Map) ->
336337
error(badarg).
337338

339+
%%-----------------------------------------------------------------------------
340+
%% @param Fun function to call with every key-value pair
341+
%% @param MapOrIterator the map or map iterator over which to iterate
342+
%% @returns `ok'
343+
%% @doc Iterate over the entries in a map.
344+
%%
345+
%% This function takes a function used to iterate over all entries in a map.
346+
%%
347+
%% This function raises a `badmap' error if `Map' is not a map or map iterator,
348+
%% and a `badarg' error if the input function is not a function.
349+
%% @end
350+
%%-----------------------------------------------------------------------------
351+
-spec foreach(
352+
Fun :: fun((Key :: key(), Value :: value()) -> any()),
353+
MapOrIterator :: map_or_iterator()
354+
) -> ok.
355+
foreach(Fun, Map) when is_function(Fun, 2) andalso is_map(Map) ->
356+
iterate_foreach(Fun, maps:next(maps:iterator(Map)));
357+
foreach(Fun, [Pos | Map] = Iterator) when
358+
is_function(Fun, 2) andalso is_integer(Pos) andalso is_map(Map)
359+
->
360+
iterate_foreach(Fun, maps:next(Iterator));
361+
foreach(_Fun, Map) when not is_map(Map) ->
362+
error({badmap, Map});
363+
foreach(_Fun, _Map) ->
364+
error(badarg).
365+
338366
%%-----------------------------------------------------------------------------
339367
%% @param Fun the function to apply to every entry in the map
340368
%% @param Map the map to which to apply the map function
@@ -463,6 +491,13 @@ iterate_fold(Fun, {Key, Value, Iterator}, Accum) ->
463491
NewAccum = Fun(Key, Value, Accum),
464492
iterate_fold(Fun, maps:next(Iterator), NewAccum).
465493

494+
%% @private
495+
iterate_foreach(_Fun, none) ->
496+
ok;
497+
iterate_foreach(Fun, {Key, Value, Iterator}) ->
498+
_ = Fun(Key, Value),
499+
iterate_foreach(Fun, maps:next(Iterator)).
500+
466501
%% @private
467502
iterate_map(_Fun, none, Accum) ->
468503
Accum;

tests/libs/estdlib/test_maps.erl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ test() ->
3737
ok = test_find(),
3838
ok = test_filter(),
3939
ok = test_fold(),
40+
ok = test_foreach(),
4041
ok = test_map(),
4142
ok = test_merge(),
4243
ok = test_remove(),
@@ -143,6 +144,42 @@ test_fold() ->
143144
?ASSERT_ERROR(maps:fold(not_a_function, any, maps:new()), badarg),
144145
ok.
145146

147+
collect_foreach(Acc) ->
148+
Self = self(),
149+
receive
150+
{Self, Key, Value} ->
151+
collect_foreach([{Key, Value} | Acc])
152+
after 0 -> Acc
153+
end.
154+
155+
test_foreach() ->
156+
% maps:foreach/2 was introduced with OTP 24.
157+
HasForeach =
158+
case erlang:system_info(machine) of
159+
"BEAM" -> erlang:function_exported(maps, foreach, 2);
160+
"ATOM" -> true
161+
end,
162+
if
163+
HasForeach ->
164+
Self = self(),
165+
Fun = fun(Key, Value) -> Self ! {self(), Key, Value} end,
166+
ok = maps:foreach(Fun, maps:new()),
167+
?ASSERT_EQUALS(collect_foreach([]), []),
168+
ok =
169+
receive
170+
{Self, _, _} -> fail
171+
after 0 -> ok
172+
end,
173+
ok = maps:foreach(Fun, #{a => 1, b => 2, c => 3}),
174+
?ASSERT_EQUALS(lists:sort(collect_foreach([])), [{a, 1}, {b, 2}, {c, 3}]),
175+
ok = check_bad_map(fun() -> maps:foreach(Fun, id(not_a_map)) end),
176+
ok = check_bad_map_or_badarg(fun() -> maps:foreach(not_a_function, id(not_a_map)) end),
177+
?ASSERT_ERROR(maps:foreach(not_a_function, maps:new()), badarg),
178+
ok;
179+
true ->
180+
ok
181+
end.
182+
146183
test_map() ->
147184
Fun = fun(_Key, Value) -> 2 * Value end,
148185
?ASSERT_EQUALS(maps:map(Fun, maps:new()), #{}),

0 commit comments

Comments
 (0)