Java сгладить вложенные карты и объединить ключи - PullRequest
0 голосов
/ 19 июня 2020

У меня есть HashMap, как показано ниже:

    Map<String,Object> map = new HashMap<>();
    Map<String,Object> map1 = new HashMap<>();
    map1.put("key1", "value1");
    Map<String,Object> map2 = new HashMap<>();
    Map<String,Object> map3 = new HashMap<>();
    map3.put("key2", "value2");
    map2.put("map3", map3);
    map.put("map1", map1);
    map.put("map2", map2);
    map.put("key3", "value3");

Я хочу сгладить его. Ожидаемый результат:

[map1.key1, value1]
[map2.map3.key2, value2]
[key3, value3]
...

Это можно сделать с помощью простых for циклов со следующим кодом:

public static Map<String, String> flat(Map<String, Object> input){
    Map<String, String> toReturn = new HashMap<>();
    for (Map.Entry<String, Object> entry: input.entrySet()) {
        if(entry.getValue() instanceof Map){
            Map<String, Object> innerMap = (Map<String, Object>)entry.getValue();
            for(Map.Entry<String, Object> innerEntry: innerMap.entrySet()) {
                if(innerEntry.getValue() instanceof Map){
                    ...
                    ...
                }
                else {
                    toReturn.put(entry.getKey() + "." + innerEntry.getKey(), innerEntry.getValue().toString());
                }
            }
        } else {
            toReturn.put(entry.getKey(), entry.getValue().toString());
        }
    }
    return toReturn;
}

Код для рекурсивного выполнения этого:

public static Map<String, String> flat(Map<String, Object> input){
    Map<String, String> toReturn = new HashMap<>();
    rec(toReturn, input, new ArrayList<>());
    return toReturn;
}

public static void rec(Map<String, String> toReturn, Map<String, Object> input, List<String> keys) {
    for (Map.Entry<String, Object> entry: input.entrySet()) {
        if(entry.getValue() instanceof Map){
            keys.add(entry.getKey());
            rec(toReturn, (Map<String, Object>) entry.getValue(), keys);
        } else {
            final StringBuffer key = new StringBuffer();
            if(keys.size() > 0) {
                keys.forEach(x -> key.append(x).append("."));
            }
            key.append(entry.getKey());
            toReturn.put(key.toString(), entry.getValue().toString());
        }
    }
    if(keys.size() > 0) {
        keys.remove(keys.size() - 1);
    }
}

Как добиться этого с помощью Java Stream API?

1 Ответ

2 голосов
/ 19 июня 2020

Это, как правило, то же решение, что и в упомянутой ссылке, но с некоторым обновлением для работы с entrySet() и добавления ключа из содержащей карту в качестве префикса:

public class FlattenMap {

    public static Stream<Map.Entry<String, Object>> flatten(Map.Entry<String, Object> entry) {
        if (entry.getValue() instanceof Map<?, ?>) {
            Map<String, Object> nested = (Map<String, Object>) entry.getValue();

            return nested.entrySet().stream()
                    .map(e -> new AbstractMap.SimpleEntry(entry.getKey() + "." + e.getKey(), e.getValue()))
                    .flatMap(FlattenMap::flatten);
        }
        return Stream.of(entry);
    }

    public static void main(String[] args) {
        Map<String,Object> map = new HashMap<>();
        Map<String,Object> map1 = new HashMap<>();
        map1.put("key1", "value1");
        Map<String,Object> map2 = new HashMap<>();
        Map<String,Object> map3 = new HashMap<>();
        map3.put("key2", "value2");
        map2.put("map3", map3);
        map.put("map1", map1);
        map.put("map2", map2);
        map.put("key3", "value3");

        // collecting to List of entries
        map.entrySet().stream()
                .flatMap(FlattenMap::flatten)
                .collect(Collectors.toList())
                .forEach(System.out::println);

        // collecting entries back to flattened map
        Map<String, Object> remapped = map.entrySet().stream()
                .flatMap(FlattenMap::flatten)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        remapped.entrySet().stream()
                .forEach(e -> System.out.printf("[%s, %s]%n", e.getKey(), e.getValue()));
    }
}

Он печатает результаты:

map2.map3.key2=value2
map1.key1=value1
key3=value3

[key3, value3]
[map2.map3.key2, value2]
[map1.key1, value1]
...