Java 8 grouping by first String attribute with different separators

Viewed 551

From an object and through Java 8 stream/collector functionality, I want to create a String made of 2 different separators. "/" to separate the options of the same category, and to separate the different categories.

Expected output result :

AJOUT option1 test / AJOUT option3 test </br> 
SUPPRESSION option2 test </br>
MODIFICATION option4 test / MODIFICATION option5 test

Notes :

  • The uppercase words (AJOUT, SUPPRESSION, MODIFICATION) are the categories and are retrieved with method option.getAction().getLabel()

  • The rest of the words stands for the option (ex: "option1 test" is an option name) and are retrieved through method option.getLabel()

Here is my try:

1- Separate all options with "/"

String description = orderCapture.getSelectedOptions().stream()
                    .map(option -> String.format("%s %s", option.getAction().getLabel(), option.getLabel()))
                    .collect(Collectors.joining(" / "));

Result

AJOUT option1 test / SUPPRESSION option2 test / AJOUT option3 test / MODIFICATION option4 test / MODIFICATION option5 test

2- Separate all options with "&lt;br/&gt" (</br>)

String description = orderCapture.getSelectedOptions().stream()
                    .map(option -> String.format("%s %s", option.getAction().getLabel(), option.getLabel()))
                    .collect(Collectors.joining(" &lt;br/&gt "));

Result

AJOUT option1 test </br> SUPPRESSION option2 test </br> AJOUT option3 test </br> MODIFICATION option4 test </br> MODIFICATION option5 test

3- My last try is to separate categories with the br tag but it is not working

String description = orderCapture.getSelectedOptions().stream()
                    .map(option -> String.format("%s %s", option.getAction().getLabel(), option.getLabel()))
                    .collect(Collectors.groupingBy(option -> option.getAction().getLabel()), 
                             Collectors.joining(" &lt;br/&gt "));

Result

Eclipse error:

  • Type mismatch: cannot convert from Collector>> to Supplier

  • The method collect(Supplier, BiConsumer, BiConsumer) in the type Stream is not applicable for the arguments (Collector>>, Collector)

Any help would be appreciated, thanks.

2 Answers

As you might have noticed the first of your implementation is useful to iterate over the list of Option if they are grouped properly. So using that as a utility method below --

private static String describeSimilarActions(List<Option> options) {
    return options.stream()
            .map(option -> String.format("%s %s", option.getAction().getLabel(), option.getLabel()))
            .collect(Collectors.joining(" / "));
}

and then grouping based on the Action label as expected to provide an appropriate line break can help as in:

String description = orderCapture.getSelectedOptions().stream()
        .collect(Collectors.groupingBy(op -> op.getAction().getLabel())) // this
        .values().stream()
        .map(opList -> describeSimilarActions(opList)) // reuse
        .collect(Collectors.joining("\n"));

This should do the trick:

Map<String, String> lines =
    orderCapture.getSelectedOptions()
                .stream()
                .collect(groupingBy(op -> op.getAction().getLabel(),
                                    mapping(op -> format("%s %s", op.getAction().getLabel(), op.getLabel()),
                                            joining(" / "))));

String description = lines.values()
                          .stream()
                          .collect(joining(" <br>\n"));

A little bit of explanation:

  1. It streams over selectedOptions
  2. Then it groups the options by op.getAction().getLabel()
  3. Once grouped it maps each element of each group into a formatted string
  4. Then it joins the elements of each group (formatted strings) into line strings
  5. After all steps the data gets collected into a map with (action label -> formatted line) pairs

The second pipeline is pretty trivial. It just joins the values (formatted lines) from each key-value pair.


These two pipelines could be combined into a single chain, but I don't think it's any sort more readable. Adding it as an example here:

String description =
    orderCapture.getSelectedOptions()
                .stream()
                .collect(groupingBy(op -> op.getAction().getLabel(),
                                    mapping(op -> format("%s %s", op.getAction().getLabel(), op.getLabel()),
                                            joining(" / "))))
                .values()
                .stream()
                .collect(joining(" <br>\n"));
Related