Reputation: 1322
I have a HashMap as below:
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");
I want to flatten it. Expected output is:
[map1.key1, value1]
[map2.map3.key2, value2]
[key3, value3]
...
This can be done using simple for
loops with following code:
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;
}
Code to do this recursively:
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);
}
}
How do I achieve this using Java Stream API ?
Upvotes: 1
Views: 2807
Reputation: 19545
This is generally the same solution as in the mentioned link but with some update to work with entrySet()
and add a key from containing map as a prefix:
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()));
}
}
It prints results:
map2.map3.key2=value2
map1.key1=value1
key3=value3
[key3, value3]
[map2.map3.key2, value2]
[map1.key1, value1]
Upvotes: 7