Java 8 nested grouping

I have 2 questions that I cannot resolve. First, I need a way to have dynamic nested grouping, where there can be 1-n nested groups that the user can pass.

The second problem is that I need the results to be flattened when the keys are concat and not 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 has a concat of the 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"
  ]
}

      

Note that keys are concat, not nested maps within maps.

I'm trying to create a grouping where it can be dynamic with no luck:

 fakeData.stream()
                .collect(groupingBy(map -> map.get("ip"),
                        groupingBy(map -> map.get("uuid"),
                                ... nested "n" times)));

      

This is the interface I'm trying to implement:

public Map<String, List<Map<String, String>>> doGrouping(List<String> columns, 
                                                   List<Map<String, String>> data);

      

+3


source to share


2 answers


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())));
}

      

I first passed data

in which is a list of cards. I immediately assembled the stream into a map of lists using Collectors.groupingBy

a key that is calculated for each element of the stream.

Calculating the key was the tricky part. To do this, I passed the given list columns

and I converted each of these columns to the corresponding value of the stream element. I did it with a method Stream.map

passing elem::map

as a display function. Finally, I have compiled this inner stream into one line using Collectors.joining

which effectively concatenates each element of the stream into a final string.



Edit: the code above works well if all elements 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())));

      

In this version, the null

elements from the stream are distinguished , which can occur if any element of the map does not contain the key specified in the list columns

.

+5


source


Not sure about using streams, but if you prefer the simple Java way it is much easier. If I understand your problem correctly, this is the method you want to build. You may need to tweak your settings a little to get it done 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}]}

      

+1


source







All Articles