Manually chain GroupBy collectors

Thomas Z.

I want to group a list of person's. A person have some attributes like name, country, town, zipcode, etc. I wrote the static code, which works very well:

Object groupedData = data.stream().collect(groupingBy(Person::getName, Collectors.groupingBy(Person::getCountry, Collectors.groupingBy(Person::getTown))));

But the problem is, that is it not dynamic. Sometimes I want to just group by name and town, sometimes by a attributes. How can I do this? Non Java 8 solutions are welcome as well.

tobias_k

You could create a function taking an arbitrary number of attributes to group by and construct the groupingBy-Collector in a loop, each time passing the previous version of itself as the downstream collector.

public static <T> Map collectMany(List<T> data, Function<T, ?>... groupers) {
    Iterator<Function<T, ?>> iter = Arrays.asList(groupers).iterator();
    Collector collector = Collectors.groupingBy(iter.next());
    while (iter.hasNext()) {
        collector = Collectors.groupingBy(iter.next(), collector);
    }
    return (Map) data.stream().collect(collector);
}

Note that the order of the grouper functions is reversed, so you have to pass them in reversed order (or reverse them inside the function, e.g. using Collections.reverse or Guava's Lists.reverse, whichever you prefer).

Object groupedData = collectMany(data, Person::getTown, Person::getCountry, Person::getName);

Or like this, using an old-school for loop to reverse the array in the function, i.e. you don't have to pass the groupers in inverse order (but IMHO this is harder to comprehend):

public static <T> Map collectMany(List<T> data, Function<T, ?>... groupers) {
    Collector collector = Collectors.groupingBy(groupers[groupers.length-1]);
    for (int i = groupers.length - 2; i >= 0; i--) {
        collector = Collectors.groupingBy(groupers[i], collector);
    }
    return (Map) data.stream().collect(collector);
}

Both approaches will return a Map<?,Map<?,Map<?,T>>>, just as in your original code. Depending on what you want to do with that data it might also be worth considering using a Map<List<?>,T>, as suggested by Tunaki.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Manually chain GroupBy collectors

From Dev

Difficulty Manually Walking The Prototype Chain

From Dev

Collectors.groupby for Map<String,List<String>

From Dev

Using Collectors to GroupBy one field, Count and Add another field value

From Dev

Calculate STD manually using Groupby Pandas DataFrame

From Dev

Calculate STD manually using Groupby Pandas DataFrame

From Java

Julia Dataframes Groupby chain using combine

From Dev

Getting this json into the correct format chain flatten groupby

From Dev

LINQ / Multiple GroupBy. Which is the proper one, recursive usage or chain usage?

From Java

Collectors.reducing to List

From Java

Collectors.toSet() and HashSet

From Dev

Customizations vs Residue collectors

From Dev

Collectors.groupingBy and map

From Dev

Java 8 Collectors API

From Dev

Collectors groupingBy java 8

From Dev

How to extend Collectors class

From Dev

Perform flatMap operation with Collectors

From Dev

Collectors.reducing to List

From Dev

HotSpot JVM Garbage Collectors

From Dev

Collectors.groupingBy and map

From Dev

Collectors::toList cyclic inference

From Dev

Issues with Java Collectors

From Dev

Differences between Collectors.toMap() and Collectors.groupingBy() to collect into a Map

From Dev

Comparing performance of Collectors.summingLong and Collectors.counting

From Dev

Collectors collect a list of pair in java

From Dev

Parallel streams, collectors and thread safety

From Dev

Map values in Collectors.groupingBy()

From Dev

Alternative to Collectors.groupingBy() for scala

From Dev

Collectors.groupingBy into list of objects?