Reputation: 18594
I have a nested HashMap
in this form:
{key1=val1, key2=val2,
key3=[
{key4=val4, key5=val5},
{key6=val6, key7=val7}
]
}
I now want to flatten that map, so that all entries are on the same level:
{key1=val1, key2=val2, key4=val4, key5=val5,key6=val6, key7=val7}
When I try
map.values().forEach(map.get("key3")::addAll);
as described in this post, I get the following error:
invalid method reference
cannot find symbol
symbol: method addAll(T)
location: class Object
where T is a type-variable:
T extends Object declared in interface Iterable
Is there any generic way to flatten any given Map
?
Upvotes: 2
Views: 16898
Reputation: 22128
Not sure if I understood the question correctly, but something like this might work. Haven't checked all the syntax yet, so there might be some mistake somewhere.
Stream<Map.Entry<String, String>> flatten(Map<String, Object> map) {
return map.entrySet()
.stream()
.flatMap(this::extractValue);
}
Stream<Map.Entry<String, String>> extractValue(Map.Entry<String, Object> entry) {
if (entry.getValue() instanceof String) {
return Stream.of(new AbstractMap.SimpleEntry(entry.getKey(), (String) entry.getValue()));
} else if (entry.getValue() instanceof Map) {
return flatten((Map<String, Object>) entry.getValue());
}
}
Then you could do:
Map<String, String> flattenedMap = flatten(yourmap)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Upvotes: 8
Reputation: 19926
You can make use of a recursive helper method:
static void forEachValue(Map<String, Object> source, BiConsumer<? super String, ? super Object> action) {
for (final Map.Entry<String, Object> entry : source.entrySet()) {
if (entry.getValue() instanceof Map) {
forEachValue((Map<String, Object>) entry.getValue(), action);
} else {
action.accept(entry.getKey(), entry.getValue());
}
}
}
Which then can be called like this:
Map<String, Object> map = ...;
Map<String, Object> flattened = new HashMap<>();
forEachValue(map, map::put);
I've used this approach with the BiConsumer
to not limit the method to only flatten the nested map into another map, but the caller may decide himself what he wants to do with every key-value pair.
Upvotes: 1
Reputation: 1454
You should try this:
Map<String, Object> flatenedMap = new HashMap<>();
map.forEach((key, value) -> {
if(value instanceof Map) {
flatenedMap.putAll((Map) value);
} else {
flatenedMap.put(key, value);
}
});
If you have more than one level of nesting you can use recursive alg.
static Map<String, Object> flatMap(Map<String, Object> map) {
Map<String, Object> flatenedMap = new HashMap<>();
map.forEach((key, value) -> {
if(value instanceof Map) {
flatenedMap.putAll(flatMap((Map) value));
} else {
flatenedMap.put(key, value);
}
});
return flatenedMap;
}
Upvotes: 0