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

openSUSE Build Service is sponsored by