Search code examples
javalambdajava-8java-streamcollectors

Java 8 Collectors.groupingBy with mapped value to set collecting result to the same set


Objects are used in example are from package org.jsoup.nodes

import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

I need group attributes by key with resulting value Set.

Optional<Element> buttonOpt = ...;
Map<String, Set<String>> stringStringMap =
    buttonOpt.map(button -> button.attributes().asList().stream()
            .collect(groupingBy(Attribute::getKey, 
                  mapping(attribute -> attribute.getValue(), toSet()))))
            .orElse(new HashMap<>());

It seems collected correctly, but value all the time is single string (because of library implementation) that contains different values split by space. Trying to improve solution:

Map<String, Set<HashSet<String>>> stringSetMap = buttonOpt.map(
        button -> button.attributes()
            .asList()
            .stream()
            .collect(groupingBy(Attribute::getKey, 
                        mapping(attribute -> 
                          new HashSet<String>(Arrays.asList(attribute.getValue()
                                                                .split(" "))),
                   toSet()))))
  .orElse(new HashMap<>());

In result i've got different structure Map<String, Set<HashSet<String>>> but i need Map<String, Set<String>>

I've checked some collectors but have not managed my issue.

Question is:

How to merge all sets that related to the same attribute key?


Solution

  • You can split your attributes with flatMap and create new entries to group:

    Optional<Element> buttonOpt = ...
    Map<String, Set<String>> stringStringMap =
            buttonOpt.map(button -> 
                button.attributes()
                      .asList()
                      .stream()
                      .flatMap(at -> Arrays.stream(at.getValue().split(" "))
                                           .map(v -> new SimpleEntry<>(at.getKey(),v)))
                      .collect(groupingBy(Map.Entry::getKey, 
                                          mapping(Map.Entry::getValue, toSet()))))
                    .orElse(new HashMap<>());