[go: up one dir, main page]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability for hierarchical grouping to maps:groups_from_list #8830

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Maria-12648430
Copy link
Contributor

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:

Data = [
    #{continent => asia, country => japan, city => tokyo},
    #{continent => europe, country => germany, city => berlin},
    #{continent => europe, country => germany, city => munich},
    ä{continent => europe, country => sweden, city => stockholm}
].

... into a structure like this:

> maps:groups_from_list([fun(#{continent := Continent}) -> Continent end,
                         fun(#{country := Country}) -> Country end],
                        fun(#{city := City}) -> City end,
                        Data).
#{asia => #{japan => [tokyo]},
  europe => #{germany => [belin, munich]
              sweden => [stockholm]}}

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 via maps: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 the KeyFuns list is not a 1-ary function, or the list to group on is not a proper list) must originate from the maps: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;

  • a call like maps:groups_from_list([], List) succeeds and returns List if it is a list, that is, even if it is an improper list; at the same time, a similar call like maps:groups_from_list([], ValueFun, List) succeeds and returns List mapped by ValueFun if and only if List is a proper list, and fails if List is not a proper list.
  • calls to maps:from_list/2,3, when the given List 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 given List (ie, the generation of the first tier) if the list of key functions contains an even number of elements.

Copy link
Contributor
github-actions bot commented Sep 19, 2024

CT Test Results

    2 files     95 suites   55m 33s ⏱️
2 157 tests 2 109 ✅ 48 💤 0 ❌
2 516 runs  2 466 ✅ 50 💤 0 ❌

Results for commit 038e924.

♻️ This comment has been updated with latest results.

To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass.

See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally.

Artifacts

// Erlang/OTP Github Action Bot

Co-authored-by: Jan Uhlig <juhlig@hnc-agency.org>
@IngelaAndin IngelaAndin added the team:VM Assigned to OTP team VM label Sep 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
team:VM Assigned to OTP team VM
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants