3 votes

Java 8 nested groupingby (regroupement imbriqué)

J'ai deux problèmes que je n'arrive pas à résoudre. Le premier est que j'ai besoin d'un moyen d'avoir des groupes imbriqués dynamiques par lesquels il pourrait y avoir 1-n groupes imbriqués que l'utilisateur peut passer.

Le deuxième problème est que j'ai besoin que les résultats soient aplatis et que les clés soient concatées plutôt qu'imbriquées.

Mon exemple d'entrée ressemble à ceci :

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

Le résultat final est un concat des clés de la carte :

{
  "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"
  ]
}

Remarquez que les clés sont concatées plutôt que des cartes imbriquées les unes dans les autres.

J'essaie de créer un groupingby où il peut être dynamique, mais sans succès :

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

Voici l'interface que j'essaie de mettre en œuvre :

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

5voto

Essayez ce qui suit :

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

Tout d'abord, j'ai diffusé le data qui est une liste de cartes. J'ai immédiatement converti le flux en une carte de listes en utilisant Collectors.groupingBy avec une clé calculée pour chaque élément du flux.

Le calcul de la clé a été la partie la plus délicate. Pour ce faire, j'ai transmis en continu la liste donnée de columns et j'ai transformé chacune de ces colonnes en la valeur correspondante de l'élément du flux. Je l'ai fait au moyen de la fonction Stream.map en passant par la méthode elem::map en tant que fonction de mise en correspondance. Enfin, j'ai rassemblé ce flux interne en une seule chaîne de caractères en utilisant Collectors.joining qui concatène chaque élément du flux en une chaîne finale de manière efficace.

Edit : Le code ci-dessus fonctionne bien si tous les éléments de columns existent en tant que clés des éléments de la carte dans data . Pour plus de sécurité, utilisez ce qui suit :

return data.stream()
    .collect(Collectors.groupingBy(
        elem -> columns.stream()
            .map(elem::get)
            .filter(Objects::nonNull)
            .collect(Collectors.joining())));

Cette version filtre null à partir du flux, ce qui peut se produire si un élément de la carte ne contient pas une clé spécifiée dans l'attribut columns liste.

1voto

Amit Phaltankar Points 1935

Je ne suis pas sûr de pouvoir utiliser les flux, mais si vous préférez la méthode Java, c'est beaucoup plus facile. Si je comprends bien votre problème, voici la méthode que vous voulez construire. Il se peut que vous deviez la modifier quelque peu pour la rendre plus rapide.

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}]}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X