Add ability for hierarchical grouping to maps:groups_from_list
#8830
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR is a suggestion for an extension to
maps:groups_from_list/2,3
to create nested groups, or a group hierarchy (also see here).With this change,
maps:groups_from_list/2,3
will also accept lists of instead of only a single key function. The result, accordingly, is a hierarchy of groups, with the keys and groupings on each tier given by the respective key function.Thus, it is easy to turn, for example, a list of data rows (as may be obtained from a database), like this:
... into a structure like this:
While something like this is also doable with the current implementation of
maps:groups_from_list/2,3
alone, it involves a significant amount of boilerplate code, which is tiresome and error-prone to write as well as hard to read, understand, and change. Having done this a couple of times in the past, I was thinking that maybe there should be a general out-of-the-box way to do it.Another performance-related problem with repeated calls to
maps:groups_from_list/2,3
(usually viamaps:map
calls for hierarchical groupings) is that each of those calls results in a list reversal, in order to return the grouped elements in the same order as in the input list. However, from the outside perspective, there is at most 1 reversal required, namely when the number of sub-groupings (ie, the number of tiers in the result hierarchy) is odd: two subsequent reversals will cancel each other out.The proposal here addresses both of the problems above: no boilerplate code necessary, just give a list of key functions; reduce the number of list reversals to the required minimum, either one or zero.
The implementation we present here is a bit cumbersome. This is because of the way errors have to be for
erl_stdlib_errors
to work. That is, an error related to invalid input (like, an element of theKeyFuns
list is not a 1-ary function, or the list to group on is not a proper list) must originate from themaps:groups_from_list/2,3
call. At the same time, errors happing in any of the given key or value functions must be passed through.This culminates in a few peculiarities;
maps:groups_from_list([], List)
succeeds and returnsList
if it is a list, that is, even if it is an improper list; at the same time, a similar call likemaps:groups_from_list([], ValueFun, List)
succeeds and returnsList
mapped byValueFun
if and only ifList
is a proper list, and fails ifList
is not a proper list.maps:from_list/2,3
, when the givenList
is an improper list, will fail early when the list of key functions contains an odd number of elements, but will fail only after the first iteration over the givenList
(ie, the generation of the first tier) if the list of key functions contains an even number of elements.