Map values in Collectors.groupingBy()

11,524

I found a solution; It involves Collections.mapping(), which can wrap a collector and apply mapping function over stream to supply elements to the wrapped collector:

static <T, U> Map<T, Set<U>> groupSecondByFirst(Collection<Tuple<T, U>> tuples) {
    return tuples
        .stream()
        .collect(
            Collectors.groupingBy(
                Tuple::getFirst,
                Collectors.mapping(
                    Tuple::getSecond,
                    Collectors.toSet())));
}
Share:
11,524
Feuermurmel
Author by

Feuermurmel

I work with computers.

Updated on June 06, 2022

Comments

  • Feuermurmel
    Feuermurmel almost 2 years

    For the sake of this example, let's assume I have a simple type Tuple with two attributes:

    interface Tuple<T, U> {
        T getFirst();
        U getSecond();
    }
    

    Now I want to transform a collection of (first, second) tuples into a map which maps each first value to a set of all second values contained in tuples with that specific first value. The method groupSecondByFirst() shows a possible implementation doing what I want:

    <T, U> Map<T, Set<U>> groupSecondByFirst(Set<Tuple<T, U>> tuples) {
        Map<T, Set<U>> result = new HashMap<>();
    
        for (Tuple<T, U> i : tuples) {
            result.computeIfAbsent(i.getFirst(), x -> new HashSet<>()).add(i.getSecond());
        }
    
        return result;
    }
    

    If the input was [(1, "one"), (1, "eins"), (1, "uno"), (2, "two"), (3, "three")] the output would be { 1 = ["one", "eins", "uno"], 2 = ["two"], 3 = ["three"] }

    I would like to know whether and how I can implement this using the streams framework. The best I got is the following expression, which returns a map which contains the full tuple as values and not just their second elements:

    Map<T, Set<Tuple<T, U>>> collect = tuples.stream().collect(
        Collectors.groupingBy(Tuple::getFirst, Collectors.toSet()));