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);
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.
Not sure about using streams, but if you prefer plain java way, it is a lot easier. If I correctly understand your problem here is the method you want to build. You may be required to tweak in a bit to make it faster.
public Map<String, List<Map<String, String>>> doGrouping(List<String> columns, List<Map<String, String>> data) {
Map<String, List<Map<String, String>>> output = new HashMap<>();
for (Map<String, String> map : data) {
String key = "";
for(String column : columns) key += "".equals(key) ? (map.get(column)) : (":" + map.get(column));
output.computeIfAbsent(key, k -> Arrays.asList(map));
}
return output;
}
Test:
doGrouping(Arrays.asList("ip", "group"), fakeData)
>> {10.0.1.1:user=[{uid=tiger, ip=10.0.1.1, group=user}, {uid=woods, ip=10.0.1.1, group=user}], 10.0.1.0:admin=[{uid=root, ip=10.0.1.0, group=admin}]}
doGrouping(Arrays.asList("group"), fakeData)
>> {admin=[{uid=root, ip=10.0.1.0, group=admin}], user=[{uid=tiger, ip=10.0.1.1, group=user}, {uid=woods, ip=10.0.1.1, group=user}]}
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