Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 800
Sergii Avatar asked Nov 27 '18 08:11

Sergii


People also ask

How does collectors groupingBy work?

The groupingBy() method of Collectors class in Java are used for grouping objects by some property and storing results in a Map instance. In order to use it, we always need to specify a property by which the grouping would be performed. This method provides similar functionality to SQL's GROUP BY clause.

Which downstream collector can count the values?

Collector collectingAndThen(Collector downstream, Function finisher): This method allows us to perform another operation on the result after collecting the input element of collection. 6. Collector counting(): It counts the number of input elements of type T and returns a Collector.

How does Groupby work in Java 8?

In Java 8, you retrieve the stream from the list and use a Collector to group them in one line of code. It's as simple as passing the grouping condition to the collector and it is complete. By simply modifying the grouping condition, you can create multiple groups.

What implementation of list does the collectors toList () create?

The toList() method of Collectors Class is a static (class) method. It returns a Collector Interface that gathers the input data onto a new list. This method never guarantees type, mutability, serializability, or thread-safety of the returned list but for more control toCollection(Supplier) method can be used.


2 Answers

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<>());
like image 133
Eran Avatar answered Oct 11 '22 10:10

Eran


Here's a Java9 way of doing it,

Map<String, Set<String>> stringSetMap = buttonOpt
    .map(button -> button.attributes().asList().stream()
        .collect(Collectors.groupingBy(Attribute::getKey, Collectors.flatMapping(
            attribute -> Arrays.stream(attribute.getValue().split(" ")), Collectors.toSet()))))
    .orElse(Collections.emptyMap());
like image 43
Ravindra Ranwala Avatar answered Oct 11 '22 10:10

Ravindra Ranwala