Last active
January 31, 2023 11:50
-
-
Save fabjan/dcdf68461926faba3a91d3ea024f6cbf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(mapmap). | |
%% Extract all key paths from a nested map, sorted. | |
%% | |
%% Example: | |
%% | |
%% > mapmap:key_paths(#{a => 1, b => #{c => 2, d => 3}}). | |
%% [[a], [b, c], [b, d]] | |
key_paths(Map) -> | |
lists:sort(key_paths(Map, [])). | |
key_paths(Map, ParentPath) when is_map(Map) -> | |
lists:foldl( | |
fun(Key, Acc) -> | |
ChildValue = maps:get(Key, Map), | |
ChildPath = ParentPath ++ [Key], | |
case key_paths(ChildValue, ChildPath) of | |
[] -> Acc; | |
ChildPaths -> Acc ++ ChildPaths | |
end | |
end, | |
[], | |
maps:keys(Map) | |
); | |
key_paths(_, Path) -> | |
[Path]. | |
%% Compare the shape of two nested maps. | |
%% Returns a list of key paths that only exist in the first map, and a list of | |
%% key paths that only exist in the second map. | |
%% The values in the maps don't matter. | |
%% | |
%% Example: | |
%% > mapmap:key_diff(#{a => 1, b => #{c => 2, d => 3}}, #{a => 1, b => #{c => 2}}). | |
%% {[[b, d]], []} | |
%% > mapmap:key_diff(#{a => 1, b => #{c => 2}}, #{a => 1, b => #{c => 2, d => 3}}). | |
%% {[], [[b, d]]} | |
key_diff(Map1, Map2) -> | |
KeysInMap1 = key_paths(Map1), | |
KeysInMap2 = key_paths(Map2), | |
{lists:usort(KeysInMap1 -- KeysInMap2), lists:usort(KeysInMap2 -- KeysInMap1)}. | |
%% For a list of maps, count the number of times each key path appears. | |
%% Returns a map of key paths to counts. | |
%% The values in the maps don't matter. | |
%% | |
%% Example: | |
%% > mapmap:key_counts([#{a => 1, b => #{c => 2, d => 3}}, #{a => 1, b => #{c => 2}}]). | |
%% #{[a] => 2, [b, c] => 2, [b, d] => 1} | |
%% > mapmap:key_counts([#{a => 1, b => #{c => 2, d => 3}}, #{a => 1, b => #{c => 2, d => 3}}]). | |
%% #{[a] => 2, [b, c] => 2, [b, d] => 2} | |
key_counts(Maps) -> | |
lists:foldl( | |
fun(Map, Acc) -> | |
lists:foldl( | |
fun(KeyPath, Acc2) -> | |
maps:update_with(KeyPath, fun(C) -> C + 1 end, 1, Acc2) | |
end, | |
Acc, | |
key_paths(Map) | |
) | |
end, | |
#{}, | |
Maps | |
). | |
%% ------------------------------------------------------------------- | |
%% Tests | |
%% ------------------------------------------------------------------- | |
-ifdef(TEST). | |
-include_lib("eunit/include/eunit.hrl"). | |
key_paths_test() -> | |
?assertEqual( | |
[[a], [b, c], [b, d]], | |
key_paths(#{a => 1, b => #{c => 2, d => 3}}) | |
). | |
really_deep_path_test() -> | |
?assertEqual( | |
[[a], [b, c, d, e]], | |
key_paths(#{a => 1, b => #{c => #{d => #{e => 1}}}}) | |
). | |
really_wide_path_test() -> | |
?assertEqual( | |
[[a], [b], [c], [d], [e], [f], [g], [h], [i], [j], [k], [l], [m], [n], [o], [p], [q], [r], [s], [t], [u], [v], [w], [x], [y], [z]], | |
key_paths(#{a => 1, b => 1, c => 1, d => 1, e => 1, f => 1, g => 1, h => 1, i => 1, j => 1, k => 1, l => 1, m => 1, n => 1, o => 1, p => 1, q => 1, r => 1, s => 1, t => 1, u => 1, v => 1, w => 1, x => 1, y => 1, z => 1}) | |
). | |
key_diff_test() -> | |
?assertEqual( | |
{[[b, d]], []}, | |
key_diff(#{a => 1, b => #{c => 2, d => 3}}, #{a => 1, b => #{c => 2}}) | |
), | |
?assertEqual( | |
{[], [[b, d]]}, | |
key_diff(#{a => 1, b => #{c => 2}}, #{a => 1, b => #{c => 2, d => 3}}) | |
). | |
longer_key_diff_test() -> | |
?assertEqual( | |
{[[b, d], [b, e]], []}, | |
key_diff(#{a => 1, b => #{c => 2, d => 3, e => 4}}, #{a => 1, b => #{c => 2}}) | |
), | |
?assertEqual( | |
{[], [[b, d], [b, e]]}, | |
key_diff(#{a => 1, b => #{c => 2}}, #{a => 1, b => #{c => 2, d => 3, e => 4}}) | |
). | |
key_counts_test() -> | |
?assertEqual( | |
#{[a] => 2, [b, c] => 2, [b, d] => 1}, | |
key_counts([#{a => 1, b => #{c => 2, d => 3}}, #{a => 1, b => #{c => 2}}]) | |
), | |
?assertEqual( | |
#{[a] => 2, [b, c] => 2, [b, d] => 2}, | |
key_counts([#{a => 1, b => #{c => 2, d => 3}}, #{a => 1, b => #{c => 2, d => 3}}]) | |
). | |
-endif. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment