File 3931-Add-maps-groups_from_list.patch of Package erlang
From 306f8bd8dbba91632d1bec536d7a0288fd1b8ee4 Mon Sep 17 00:00:00 2001
From: Gian Lorenzo Meocci <glmeocci@gmail.com>
Date: Thu, 6 Jan 2022 15:23:32 +0100
Subject: [PATCH] Add maps:groups_from_list
---
lib/stdlib/doc/src/maps.xml | 36 ++++++++++++++-
lib/stdlib/src/erl_stdlib_errors.erl | 4 ++
lib/stdlib/src/maps.erl | 66 +++++++++++++++++++++++++++-
lib/stdlib/test/maps_SUITE.erl | 23 +++++++++-
4 files changed, 125 insertions(+), 4 deletions(-)
diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml
index ea0f16eec9..a8793e86d1 100644
--- a/lib/stdlib/doc/src/maps.xml
+++ b/lib/stdlib/doc/src/maps.xml
@@ -606,7 +606,7 @@ error</code>
maps:update_with("new counter",Fun,42,Map).
#{"counter" => 1,"new counter" => 42}</code>
</desc>
- </func>
+ </func>
<func>
<name name="values" arity="1"/>
@@ -657,5 +657,39 @@ error</code>
#{1337 => "value two"}</code>
</desc>
</func>
+
+ <func>
+ <name name="groups_from_list" arity="2"/>
+ <fsummary>Splits the list into groups using a function as discriminator.</fsummary>
+ <desc>
+ <p>The result is a map where each key is given by <anno>Fun</anno>
+ and each value is a list of elements. The order of elements within
+ each list is preserved from the list.</p>
+ <p><em>Examples:</em></p>
+ <pre>
+> <input>maps:groups_from_list(fun(X) -> X rem 2 end, [1,2,3]).</input>
+#{0 => [2], 1 => [1, 3]}
+> <input>maps:groups_from_list(fun erlang:length/1, ["ant", "buffalo", "cat", "dingo"]).</input>
+#{3 => ["ant", "cat"], 5 => ["dingo"], 7 => ["buffalo"]}</pre>
+ </desc>
+ </func>
+
+ <func>
+ <name name="groups_from_list" arity="3"/>
+ <fsummary>Splits the list into groups using a function as discriminator.</fsummary>
+ <desc>
+ <p>The result is a map where each key is given by
+ <anno>Fun</anno> and each value is a list of elements given by
+ the <anno>ValueFun</anno>. The order of elements within each
+ list is preserved from the list.</p>
+ <p><em>Examples:</em></p>
+ <pre>
+> <input>maps:groups_from_list(fun(X) -> X rem 2 end, fun(X) -> X*X end, [1,2,3]).</input>
+#{0 => [4], 1 => [1, 9]}
+> <input>maps:groups_from_list(fun erlang:length/1, fun lists:reverse/1, ["ant", "buffalo", "cat", "dingo"]).</input>
+#{3 => ["tna","tac"],5 => ["ognid"],7 => ["olaffub"]}</pre>
+ </desc>
+ </func>
+
</funcs>
</erlref>
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index e6192eb22b..e3fb532750 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -26,7 +26,8 @@
-export([get/3, filter/2,fold/3,
map/2, size/1,
update_with/3, update_with/4,
- without/2, with/2]).
+ without/2, with/2,
+ groups_from_list/2, groups_from_list/3]).
%% BIFs
-export([get/2, find/2, from_list/1,
@@ -497,5 +498,68 @@ with_1([K|Ks], Map) ->
end;
with_1([], _Map) -> [].
+%% groups_from_list/2 & groups_from_list/3
+
+-spec groups_from_list(Fun, List) -> MapOut when
+ Fun :: fun((Elem :: T) -> Selected),
+ MapOut :: #{Selected => List},
+ Selected :: term(),
+ List :: [T],
+ T :: term().
+
+groups_from_list(Fun, List0) when is_function(Fun, 1) ->
+ try lists:reverse(List0) of
+ List ->
+ groups_from_list_1(Fun, List, #{})
+ catch
+ error:_ ->
+ erlang:error(badarg, [Fun, List0])
+ end;
+groups_from_list(Fun, List) ->
+ erlang:error(badarg, [Fun, List]).
+
+groups_from_list_1(Fun, [H | Tail], Acc) ->
+ K = Fun(H),
+ NewAcc = case Acc of
+ #{K := Vs} -> Acc#{K := [H | Vs]};
+ #{} -> Acc#{K => [H]}
+ end,
+ groups_from_list_1(Fun, Tail, NewAcc);
+groups_from_list_1(_Fun, [], Acc) ->
+ Acc.
+
+-spec groups_from_list(Fun, ValueFun, List) -> MapOut when
+ Fun :: fun((Elem :: T) -> Key),
+ ValueFun :: fun((Elem :: T) -> ValOut),
+ MapOut :: #{Key := ListOut},
+ Key :: term(),
+ ValOut :: term(),
+ List :: [T],
+ ListOut :: [ValOut],
+ T :: term().
+
+groups_from_list(Fun, ValueFun, List0) when is_function(Fun, 1),
+ is_function(ValueFun, 1) ->
+ try lists:reverse(List0) of
+ List ->
+ groups_from_list_2(Fun, ValueFun, List, #{})
+ catch
+ error:_ ->
+ erlang:error(badarg, [Fun, ValueFun, List0])
+ end;
+groups_from_list(Fun, ValueFun, List) ->
+ erlang:error(badarg, [Fun, ValueFun, List]).
+
+groups_from_list_2(Fun, ValueFun, [H | Tail], Acc) ->
+ K = Fun(H),
+ V = ValueFun(H),
+ NewAcc = case Acc of
+ #{K := Vs} -> Acc#{K := [V | Vs]};
+ #{} -> Acc#{K => [V]}
+ end,
+ groups_from_list_2(Fun, ValueFun, Tail, NewAcc);
+groups_from_list_2(_Fun, _ValueFun, [], Acc) ->
+ Acc.
+
error_type(M) when is_map(M) -> badarg;
error_type(V) -> {badmap, V}.
diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl
index 28895c4efe..2492130cf1 100644
--- a/lib/stdlib/test/maps_SUITE.erl
+++ b/lib/stdlib/test/maps_SUITE.erl
@@ -42,7 +42,8 @@
t_get_3/1, t_filter_2/1,
t_fold_3/1,t_map_2/1,t_size_1/1,
t_put_opt/1, t_merge_opt/1,
- t_with_2/1,t_without_2/1]).
+ t_with_2/1,t_without_2/1,
+ t_groups_from_list/1]).
%%-define(badmap(V,F,Args), {'EXIT', {{badmap,V}, [{maps,F,Args,_}|_]}}).
%%-define(badarg(F,Args), {'EXIT', {badarg, [{maps,F,Args,_}|_]}}).
@@ -68,7 +69,8 @@ all() ->
t_get_3,t_filter_2,
t_fold_3,t_map_2,t_size_1,
t_put_opt,t_merge_opt,
- t_with_2,t_without_2].
+ t_with_2,t_without_2,
+ t_groups_from_list].
t_update_with_3(Config) when is_list(Config) ->
V1 = value1,
@@ -770,4 +772,14 @@ t_size_1(Config) when is_list(Config) ->
?badmap(<<>>,size,[<<>>]) = (catch maps:size(id(<<>>))),
ok.
+t_groups_from_list(_Config) ->
+ #{} = maps:groups_from_list(fun erlang:length/1, []),
+ #{3 := ["tna","tac"], 5 := ["ognid"], 7 := ["olaffub"]} =
+ maps:groups_from_list(
+ fun erlang:length/1,
+ fun lists:reverse/1,
+ ["ant", "buffalo", "cat", "dingo"]
+ ),
+ #{0 := [2], 1 := [1, 3]} = maps:groups_from_list(fun(X) -> X rem 2 end, [1, 2, 3]).
+
id(I) -> I.
--
2.34.1