fix(utils): make `flattermap/2` results less variative

This commit is contained in:
Andrew Mayorov 2023-12-01 15:05:42 +03:00
parent 6255ee0833
commit 29ec73847a
No known key found for this signature in database
GPG Key ID: 2837C62ACFBFED5D
2 changed files with 27 additions and 34 deletions

View File

@ -1000,35 +1000,27 @@ search(ExpectValue, KeyFunc, [Item | List]) ->
false -> search(ExpectValue, KeyFunc, List) false -> search(ExpectValue, KeyFunc, List)
end. end.
%% @doc Maps over a term or a list of terms and flattens the result, giving back %% @doc Maps over a list of terms and flattens the result, giving back a flat
%% again a term or a flat list of terms. It's similar to `lists:flatmap/2`, but %% list of terms. It's similar to `lists:flatmap/2`, but it also works on a
%% it works on a single term as well, both as input and `Fun` output (thus, the %% single term as `Fun` output (thus, the wordplay on "flatter").
%% wordplay on "flatter").
%% The purpose of this function is to adapt to `Fun`s that return either a `[]` %% The purpose of this function is to adapt to `Fun`s that return either a `[]`
%% or a term, and to avoid costs of list construction and flattening when dealing %% or a term, and to avoid costs of list construction and flattening when
%% with large lists. %% dealing with large lists.
-spec flattermap(Fun, FlatList) -> FlatList when -spec flattermap(Fun, [X]) -> [X] when
Fun :: fun((X) -> FlatList), Fun :: fun((X) -> [X] | X).
FlatList :: [X] | X.
flattermap(_Fun, []) -> flattermap(_Fun, []) ->
[]; [];
flattermap(Fun, [X | Xs]) -> flattermap(Fun, [X | Xs]) ->
flatcomb(Fun(X), flattermap(Fun, Xs)); flatcomb(Fun(X), flattermap(Fun, Xs)).
flattermap(Fun, X) ->
Fun(X).
flatcomb([], Z) -> flatcomb([], Zs) ->
Z; Zs;
flatcomb(Y, []) -> flatcomb(Ys = [_ | _], []) ->
Y; Ys;
flatcomb(Ys = [_ | _], Zs = [_ | _]) -> flatcomb(Ys = [_ | _], Zs = [_ | _]) ->
Ys ++ Zs; Ys ++ Zs;
flatcomb(Ys = [_ | _], Z) -> flatcomb(Y, Zs) ->
Ys ++ [Z]; [Y | Zs].
flatcomb(Y, Zs = [_ | _]) ->
[Y | Zs];
flatcomb(Y, Z) ->
[Y, Z].
-ifdef(TEST). -ifdef(TEST).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").

View File

@ -233,33 +233,34 @@ t_pmap_late_reply(_) ->
ok. ok.
t_flattermap(_) -> t_flattermap(_) ->
?assertEqual(
[42],
emqx_utils:flattermap(fun identity/1, [42])
),
?assertEqual( ?assertEqual(
[42, 42], [42, 42],
emqx_utils:flattermap(fun duplicate/1, 42) emqx_utils:flattermap(fun duplicate/1, [42])
),
?assertEqual(
[],
emqx_utils:flattermap(fun nil/1, [42])
), ),
?assertEqual( ?assertEqual(
[1, 1, 2, 2, 3, 3], [1, 1, 2, 2, 3, 3],
emqx_utils:flattermap(fun duplicate/1, [1, 2, 3]) emqx_utils:flattermap(fun duplicate/1, [1, 2, 3])
), ),
?assertEqual(
[],
emqx_utils:flattermap(fun nil/1, 42)
),
?assertEqual( ?assertEqual(
[], [],
emqx_utils:flattermap(fun nil/1, [1, 2, 3]) emqx_utils:flattermap(fun nil/1, [1, 2, 3])
), ),
?assertEqual( ?assertEqual(
42, [1, 2, 2, 4, 5, 5],
emqx_utils:flattermap(fun identity/1, [42])
),
?assertEqual(
[1, 3, 5],
emqx_utils:flattermap( emqx_utils:flattermap(
fun(X) -> fun(X) ->
case X rem 2 of case X rem 3 of
0 -> []; 0 -> [];
1 -> X 1 -> X;
2 -> [X, X]
end end
end, end,
[1, 2, 3, 4, 5] [1, 2, 3, 4, 5]