File 2202-Add-proplists-to_map-2-including-normalization.patch of Package erlang
From 0dc222a1cdbff5386212f262a7d1117cabb28f71 Mon Sep 17 00:00:00 2001
From: Maria-12648430 <maria-12648430@gmx.net>
Date: Wed, 16 Dec 2020 17:47:58 +0100
Subject: [PATCH 2/2] Add proplists:to_map/2 including normalization
---
lib/stdlib/doc/src/proplists.xml | 12 +++
lib/stdlib/src/proplists.erl | 44 ++++++++---
lib/stdlib/test/proplists_SUITE.erl | 112 +++++++++++++++++++---------
3 files changed, 123 insertions(+), 45 deletions(-)
diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml
index 05c02cc3be..6aed2d45e1 100644
--- a/lib/stdlib/doc/src/proplists.xml
+++ b/lib/stdlib/doc/src/proplists.xml
@@ -396,6 +396,18 @@ to_map([a, {b, 1}, {c, 2}, {c, 3}])</code>
</desc>
</func>
+ <func>
+ <name name="to_map" arity="2" since=""/>
+ <fsummary></fsummary>
+ <desc>
+ <p>Converts the property list <c><anno>List</anno></c> to a map after
+ applying the normalizations given in <c><anno>Stages</anno></c>.</p>
+ <p>See also
+ <seemfa marker="#normalize/2"><c>normalize/2</c></seemfa>,
+ <seemfa marker="#to_map/1"><c>to_map/1</c></seemfa>.</p>
+ </desc>
+ </func>
+
<func>
<name name="unfold" arity="1" since=""/>
<fsummary></fsummary>
diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl
index b92982dae0..8f25229cdd 100644
--- a/lib/stdlib/src/proplists.erl
+++ b/lib/stdlib/src/proplists.erl
@@ -35,7 +35,8 @@
lookup_all/2, is_defined/2, get_value/2, get_value/3,
get_all_values/2, append_values/2, get_bool/2, get_keys/1,
delete/2, substitute_aliases/2, substitute_negations/2,
- expand/2, normalize/2, split/2, from_map/1, to_map/1]).
+ expand/2, normalize/2, split/2, from_map/1, to_map/1,
+ to_map/2]).
%% ---------------------------------------------------------------------
@@ -607,14 +608,17 @@ flatten([]) ->
Expansions :: [{Property :: property(), Expansion :: [term()]}],
ListOut :: [term()].
-normalize(L, [{aliases, As} | Xs]) ->
- normalize(substitute_aliases(As, L), Xs);
-normalize(L, [{expand, Es} | Xs]) ->
- normalize(expand(Es, L), Xs);
-normalize(L, [{negations, Ns} | Xs]) ->
- normalize(substitute_negations(Ns, L), Xs);
-normalize(L, []) ->
- compact(L).
+normalize(L, Stages) ->
+ compact(apply_stages(L, Stages)).
+
+apply_stages(L, [{aliases, As} | Xs]) ->
+ apply_stages(substitute_aliases(As, L), Xs);
+apply_stages(L, [{expand, Es} | Xs]) ->
+ apply_stages(expand(Es, L), Xs);
+apply_stages(L, [{negations, Ns} | Xs]) ->
+ apply_stages(substitute_negations(Ns, L), Xs);
+apply_stages(L, []) ->
+ L.
%% ---------------------------------------------------------------------
@@ -702,6 +706,8 @@ to_map(List) ->
fun
({K, V}, M) ->
M#{K => V};
+ %% if tuples with arity /= 2 appear before atoms or
+ %% tuples with arity == 2, get_value/2,3 returns early
(T, M) when 1 =< tuple_size(T) ->
maps:remove(element(1, T), M);
(K, M) when is_atom(K) ->
@@ -713,6 +719,26 @@ to_map(List) ->
List
).
+%% @doc Converts the property list <code>List</code> to a map
+%% after applying the normalizations given in <code>Stages</code>.
+%%
+%% @see normalize/2
+%% @see to_map/1
+
+-spec to_map(List, Stages) -> Map when
+ List :: [term()],
+ Stages :: [Operation],
+ Operation :: {'aliases', Aliases}
+ | {'negations', Negations}
+ | {'expand', Expansions},
+ Aliases :: [{Key, Key}],
+ Negations :: [{Key, Key}],
+ Expansions :: [{Property :: property(), Expansion :: [term()]}],
+ Map :: #{term() => term()}.
+
+to_map(List, Stages) ->
+ to_map(apply_stages(List, Stages)).
+
%% @doc Converts the map <code>Map</code> to a property list.
-spec from_map(Map) -> List when
diff --git a/lib/stdlib/test/proplists_SUITE.erl b/lib/stdlib/test/proplists_SUITE.erl
index 64c5e60125..d0ebd2423e 100644
--- a/lib/stdlib/test/proplists_SUITE.erl
+++ b/lib/stdlib/test/proplists_SUITE.erl
@@ -23,7 +23,8 @@
-export([all/0, suite/0,groups/0, init_per_suite/1, end_per_suite/1,
init_per_group/2, end_per_group/2,
init_per_testcase/2, end_per_testcase/2,
- examples/1, map_conversion/1]).
+ examples/1, map_conversion/1, map_conversion_normalize/1,
+ pm_fold_test/1]).
init_per_testcase(_Case, Config) ->
Config.
@@ -36,7 +37,7 @@ suite() ->
{timetrap,{minutes,5}}].
all() ->
- [examples, map_conversion].
+ [examples, map_conversion, map_conversion_normalize, pm_fold_test].
groups() ->
[].
@@ -103,39 +104,78 @@ map_conversion(_Config) ->
%% map yields the same results as proplists:get_value/3 on the
%% original proplist, ie they either all return the same `Value',
%% or they all return the `Default' given as respective third argument.
- Default1 = make_ref(),
- Default2 = make_ref(),
- Default3 = make_ref(),
- InList=[a, b, {a, 1}, {}, {a}, {a, 1, 2}, "foo"],
- lists:foreach(
- fun (L1) ->
- LKs = proplists:get_keys(L1),
- M = proplists:to_map(L1),
- L2 = proplists:from_map(M),
- [] = maps:keys(M) -- LKs,
- [] = proplists:get_keys(L2) -- LKs,
- lists:foreach(
- fun (K) ->
- case
- {
- maps:get(K, M, Default1),
- proplists:get_value(K, L1, Default2),
- proplists:get_value(K, L2, Default3)
- }
- of
- {V, V, V} -> true;
- {Default1, Default2, Default3} -> true
- end
- end,
- LKs
- )
+ Default = make_ref(),
+ InList=[a, b, {a, 1}, {}, {a}, {a, 1, 2}, {c, 1, 2}, "foo"],
+ Fun = fun (L1, Acc) ->
+ LKs = proplists:get_keys(L1),
+ M = proplists:to_map(L1),
+ L2 = proplists:from_map(M),
+ true = lists:sort(maps:keys(M)) =:= lists:sort(proplists:get_keys(L2)),
+ lists:foreach(
+ fun (K) ->
+ case
+ {
+ maps:get(K, M, Default),
+ proplists:get_value(K, L1, Default),
+ proplists:get_value(K, L2, Default)
+ }
+ of
+ {Default, Default, Default} -> ok;
+ {V, V, V} -> ok
+ end
+ end,
+ LKs
+ ),
+ Acc
+ end,
+ _ = pm_fold(Fun, undefined, InList),
+ ok.
+
+map_conversion_normalize(_Config) ->
+ Stages = [
+ {aliases, [{a, alias_a}]},
+ {negations, [{no_b, b}]},
+ {expand, [{c, [d]}]}
+ ],
+
+ M1 = proplists:to_map([], Stages),
+ true = M1 =:= #{},
+ true = M1 =:= proplists:to_map(proplists:normalize([], Stages)),
+
+ List = [a, no_b, c],
+ M2 = proplists:to_map(List, Stages),
+ true = M2 =:= #{alias_a => true, b => false, d => true},
+ true = M2 =:= proplists:to_map(proplists:normalize(List, Stages)),
+
+ ok.
+
+pm_fold(_, _, []) ->
+ [];
+pm_fold(Fun, Acc0, L) ->
+ pm_fold(Fun, Acc0, L, []).
+
+pm_fold(Fun, Acc, [], Mut) ->
+ Fun(Mut, Acc);
+pm_fold(Fun, Acc, L, Mut) ->
+ lists:foldl(
+ fun
+ (X, AccIn) -> pm_fold(Fun, AccIn, lists:delete(X, L), [X|Mut])
end,
- [[A, B, C, D, E, F, G] || A <- InList,
- B <- InList -- [A],
- C <- InList -- [A, B],
- D <- InList -- [A, B, C],
- E <- InList -- [A, B, C, D],
- F <- InList -- [A, B, C, D, E],
- G <- InList -- [A, B, C, D, E, F]]
- ),
+ Acc,
+ L
+ ).
+
+pm_fold_test(_Config) ->
+ Fun = fun (M, A) -> [M|A] end,
+
+ [] = pm_fold(Fun, [], []),
+
+ [[1]] = lists:sort(pm_fold(Fun, [], [1])),
+
+ Exp1 = lists:sort([[1, 2], [2, 1]]),
+ Exp1 = lists:sort(pm_fold(Fun, [], [1, 2])),
+
+ Exp2 = lists:sort([[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]),
+ Exp2 = lists:sort(pm_fold(Fun, [], [1, 2, 3])),
+
ok.
--
2.26.2