Java 8 nested groupingby
java, java-8, java-stream
Solution
Try the following:
public Map<String, List<Map<String, String>>> doGrouping(
List<String> columns,
List<Map<String, String>> data) {
return data.stream()
.collect(Collectors.groupingBy(
elem -> columns.stream()
.map(elem::get)
.collect(Collectors.joining())));
}
First, I streamed the `data`, which is a list of maps. I immediately collected the stream to a map of lists using `Collectors.groupingBy` with a key that is calculated for each element of the stream.
Calculating the key was the tricky part. For this, I streamed the given list of `columns` and I transformed each one of these columns into its corresponding value of the element of the stream. I did this by means of the `Stream.map` method, passing `elem::map` as the mapping function. Finally, I collected this inner stream into a single string by using `Collectors.joining`, which concatenates each element of the stream into a final string in an efficient manner.
Edit: The code above works well if all the elements of `columns` exist as keys of the map elements in `data`. To be more secure use the following:
return data.stream()
.collect(Collectors.groupingBy(
elem -> columns.stream()
.map(elem::get)
.filter(Objects::nonNull)
.collect(Collectors.joining())));
This version filters out `null` elements from the stream, which might occur if some map element does not contain a key specified in the `columns` list.
Problem
I have 2 issues that I can't seem to solve. The first one is I need a way to have dynamic nested grouping by where there could be 1-n nested groups that the user can pass in. The second issue is that I need the results to be flatten where the keys are concat rather than nested. My example input looks like this: ``` List<Map<String, String>> fakeData = new LinkedList<>(); Map<String, String> data1 = new HashMap<>(); data1.put("ip","10.0.1.0"); data1.put("uid","root"); data1.put("group","admin"); fakeData.add(data1); Map<String, String> data2 = new HashMap<>(); data2.put("ip","10.0.1.1"); data2.put("uid","tiger"); data2.put("group","user"); fakeData.add(data2); Map<String, String> data3 = new HashMap<>(); data3.put("ip","10.0.1.1"); data3.put("uid","woods"); data3.put("group","user"); fakeData.add(data3); ``` The end result have a concat of map keys: ``` { "10.0.1.1user": [ { "uid": "tiger", "ip": "10.0.1.1", "group": "user" }, { "uid": "woods", "ip": "10.0.1.1", "group": "user" } ], "10.0.1.0admin": [ "uid": "root", "ip": "10.0.1.0", "group": "admin" ] } ``` Notice the keys are concat rather than nested maps within maps. I'm trying to create a groupingby where it can be dynamic without any luck: ``` fakeData.stream() .collect(groupingBy(map -> map.get("ip"), groupingBy(map -> map.get("uuid"), ... nested "n" times))); ``` This is the interface that I'm trying to implement: ``` public Map<String, List<Map<String, String>>> doGrouping(List<String> columns, List<Map<String, String>> data); ```