Marko
Marko

Reputation: 79

Mapping map values in Java using streams

I have a map, Map<String, Map<String, String>> myMap = new HashMap<>(); that I would like to remap to get it's values, so that I get as a result Map<String, String>.

Is it possible to do the mapping using stream API?

I have solved the problem using a for loop but I'm interested if that could be done using streams.

My solution:

   Map<String, String> result = new HashMap<>();

    myMap.forEach((k, v) -> {
        result.putAll(v);
    });

What I want is to get all the values from myMap and put them in a new Map.

Upvotes: 2

Views: 1641

Answers (2)

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 28968

You need to flatten the entries of the nested maps which can be done using either flatMap() or mapMulty().

And then apply collect() with the minimalistic two-args flavor of Collector toMap() passed as an argument. It would be sufficient since you don't expect duplicates.

Here's an example using flatMap():

Map<String, Map<String, String>> myMap = new HashMap<>();
        
Map<String, String> res = myMap.entrySet().stream()         // stream of maps
    .flatMap(entry -> entry.getValue().entrySet().stream()) // stream of map entries
    .collect(Collectors.toMap(
        Map.Entry::getKey,  // key mapper
        Map.Entry::getValue // value mapper
    ));

Example with Java 16 mapMulti() used for flattening the data:

Map<String, Map<String, String>> myMap = new HashMap<>();
        
Map<String, String> res = myMap.entrySet().stream()   // stream of maps
    .<Map.Entry<String, String>>mapMulti((entry, consumer) -> 
        entry.getValue().entrySet().forEach(consumer) // stream of map entries
    )
    .collect(Collectors.toMap(
        Map.Entry::getKey,  // key mapper
        Map.Entry::getValue // value mapper
    ));

Upvotes: 1

Chaosfire
Chaosfire

Reputation: 6985

If you are certain there are no duplicate keys, you can do it like this.

Map<String, String> res = myMap.values()
            .stream()
            .flatMap(value -> value.entrySet().stream())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue);

If there may be duplicate keys between the inner maps, you will have to introduce merge function to resolve conflicts. Simple resolution keeping the value of the second encountered entry may look like this:

Map<String, String> res = myMap.values()
            .stream()
            .flatMap(value -> value.entrySet().stream())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2));

Basically, stream the values, which are Maps, flatten them to a stream of entries and collect the entries in a new Map.

Upvotes: 1

Related Questions