Search code examples
javajava-streamcollectors

Java groupingBy and transform Strings


I am receiving a List of Strings that I am attempting to partition with .groupingBy(), which will give me a Map<String, List<String>>, and transform the Strings that are going to end up in the Lists that are in the Map.

Examples I've found use Collectors.mapping which was what I thought to use, but all of the examples are using a method reference, like .mapping(SomeClass::getSomething, ... which, since I'm acting on Strings, can't be done.

I thought I could use a lambda as the mapping function but can't seem to get the syntax right, ending up with "Type mismatch: cannot convert..." errors.

This illustrates the kind of thing I'm trying to do —

import java.util.*;
import java.util.stream.Collectors;

class Main
{
  public static void main(String[] args)
  {
        List<String> inputs = Arrays.asList("D8", "S1", "S5", "D2", "D15", "S9");
        System.out.println(inputs);

        Map<String, List<String>>
        outputs = inputs.stream()
              .collect(
                  Collectors.groupingBy(s -> s.startsWith("D") ? "desperate" : "serious")
                  //,Collectors.mapping(n -> Integer.valueOf(n.replaceAll("[DS]", "")), Collectors.toList())
              )
              ;

        System.out.println(outputs);
  }
}

This creates the Map, grouping the input data as I expect, and produces the output
{desperate=[D8, D2, D15], serious=[S1, S5, S9]}

but what I want, as you may guess from the commented out line
Collectors.mapping(n -> Integer.valueOf(n.replaceAll("[DS]", "")), Collectors.toList())
is a Map<String, List<Integer>> containing the values extracted from the strings by removing the prefix.

My real use cases may transform one String to another String, or extract Integers from a more complex string, but this illustrates what I'm trying to do.

How can I use a lambda function to do the mapping when I'm groupingBy?
I'm also not clear on the use of the second argument toList() in Collectors.mapping from the examples I've read.

I have created a repl with this code.


Solution

  • Collectors.groupingBy() has a version that takes another collector for the values, for example:

    @Test
    public void streamsTest() {
        List<String> inputs = Arrays.asList("D8", "S1", "S5", "D2", "D15", "S9");
        System.out.println(inputs);
    
        Map<String, List<Integer>> outputs = inputs.stream()
                .collect(
                        Collectors.groupingBy(
                                s -> s.startsWith("D") ? "desperate" : "serious",
                                Collectors.mapping(
                                        s -> Integer.valueOf(s.substring(1)),
                                        Collectors.toList())));
    
        System.out.println(outputs);
    }
    

    This produces:

    [D8, S1, S5, D2, D15, S9]
    {desperate=[8, 2, 15], serious=[1, 5, 9]}