I have a series of input Strings in the following format:
typeA:code1,
typeA:code2,
typeA:code3,
typeB:code4,
typeB:code5,
typeB:code6,
typeC:code7,
...
and I need to get a Map<String, List<String>>
with the following structure:
typeA, [code1, code2, code3]
typeB, [code4, code5, code6]
typeC, [code7, code8, ...]
The catch is that to generate each type I need to call a function like this one on each input String:
public static String getType(String code)
{
return code.split(":")[0]; // yes this is horrible code, it's just for the example, honestly
}
I'm pretty confident that Streams and Collectors can do this, but I'm struggling to get the right incantation of spells to make it happen.
Here is one way to do it (assuming the class is named A
):
Map<String, List<String>> result = Stream.of(input)
.collect(groupingBy(A::getType, mapping(A::getValue, toList())));
If you want the output sorted you can use a TreeMap instead of the default HashMap:
.collect(groupingBy(A::getType, TreeMap::new, mapping(A::getValue, toList())));
Full example:
public static void main(String[] args) {
String input[] = ("typeA:code1," +
"typeA:code2," +
"typeA:code3," +
"typeB:code4," +
"typeB:code5," +
"typeB:code6," +
"typeC:code7").split(",");
Map<String, List<String>> result = Stream.of(input)
.collect(groupingBy(A::getType, mapping(A::getValue, toList())));
System.out.println(result);
}
public static String getType(String code) {
return code.split(":")[0];
}
public static String getValue(String code) {
return code.split(":")[1];
}
The code becomes simple if you consider what you have omitted, that you need the second part of the split string as well:
Map<String, List<String>> result = Stream.of(input).map(s->s.split(":", 2))
.collect(groupingBy(a->a[0], mapping(a->a[1], toList())));
(assuming you have a import static java.util.stream.Collectors.*;
)
There is nothing wrong with splitting a String
into an array, the implementation has even a “fast-path” for the common case you are splitting using a single simple character instead of a complicate regular expression.
Although I was too slow, here is an MCVE showing how this can be solved with Collectors#groupingBy
.
There are obviously different options for defining the "classifier" and "mapper". Here I'm simply using String#substring
to find the part before and after the ":"
.
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class GroupingBySubstringsTest
{
public static void main(String[] args)
{
List<String> strings = new ArrayList<String>();
strings.add("typeA:code1");
strings.add("typeA:code2");
strings.add("typeA:code3");
strings.add("typeB:code4");
strings.add("typeB:code5");
strings.add("typeB:code6");
strings.add("typeC:code7");
Map<String, List<String>> result = strings.stream().collect(
groupingBy(s -> s.substring(0, s.indexOf(":")),
mapping(s -> s.substring(s.indexOf(":")+1), toList())));
for (Entry<String, List<String>> entry : result.entrySet())
{
System.out.println(entry);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With