Alagu Narayanan K
Alagu Narayanan K

Reputation: 47

How to flatten a Map by turning its values into keys

How can I transform a Map<String, List<String>> into flattened map based on values grouped as keys.

I.e. from Map<String, List<String>> to Map<String, String> (flattened by values)

For an example.

Source map:

<"fuel", ["BMW", "Honda"]>,
<"electric", ["Tesla", "Nio"]>

Flattened map:

[  
"BMW" : "fuel",  
"Honda" : "fuel",  
"Tesla" : "electric",  
"Nio" : "electric"
]

Upvotes: 0

Views: 1326

Answers (1)

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 28968

It is not possible to have multiple keys identical according to equals/hashCode within the same map because it contradicts with idea of the map data structure. Every key must be unique, you can't store multiple entries with the same key in a Map.

But you can create a list of Map.Entry objects.

For that, you need to create a stream over the entry set and then flatten each entry by creating a new entry based on car brands (elements of a flatted value) for each key:

        Map<String, List<String>> cars =
                Map.of("fuel", List.of("BMW", "Honda"),
                        "electric", List.of("Tesla", "Nio"));

        List<Map.Entry<String, String>> entries =
                cars.entrySet().stream()
                    .flatMap(entry -> entry.getValue().stream()
                                .map(brand -> Map.entry(entry.getKey(), brand)))
                    .collect(Collectors.toList());

        System.out.println(entries);

output

[electric=Tesla, electric=Nio, fuel=BMW, fuel=Honda]

Or maybe your intention was to create a Map<String, String> that will allow to retrieve car type based on brand (like that: "BMW" : "fuel", "Honda" : "fuel") then it'll make sense.

The overall approach will be similar to the previous one:

  • create a stream over the entry set;
  • flatten each entry using flatMap() by turning an element in the value-list into a new entry;
  • collect elements to a map with Collectors.toMap().

But there's a caveat: all values have to be unique or there must be a rule on how to combine/discarde car types.

I'll make an assumption that all brands in the map are unique, otherwise, the code below will fail (to deal with collisions Collectors.toMap() requires the third argument - mergeFunction).

        Map<String, String> typeByBrand =
                cars.entrySet().stream()
                        .flatMap(entry -> entry.getValue().stream()
                                .map(brand -> Map.entry(brand, entry.getKey())))
                        .collect(Collectors.toMap(Map.Entry::getKey,
                                                  Map.Entry::getValue));

        System.out.println(typeByBrand);

output

{Nio=electric, Tesla=electric, BMW=fuel, Honda=fuel}

Upvotes: 3

Related Questions