Convert Map<A, Map<B, C>> to Map<B, Map<A, C>> using streams

Paweł Bąk :

I have structure map of maps like:

Map<Center, Map<Product, Value>> given

and I want to get

Map<Product, Map<Center, Value>> result

I've used Java streams

Map<Product, Map<Center, Value>> result = given.entrySet().stream()
        .flatMap(entry -> entry.getValue()
                 .entrySet().stream()
                 .map(e -> Triple(entry.getKey(), e.getKey(), e.getValue())))
        .collect(Collectors.groupingBy(Triple::getProduct,
                    Collectors.toMap(Triple::getCenter, Triple::getValue)));

where Triple is simple value class. My questions is if it is possible to do it functional without using additional classes like Triple or e.g. Table from guava?

Ousmane D. :

Unfortunately, if you want to proceed with your stream approach it's unavoidable to create some type of intermediate object i.e. Triple, or AbstractMap.SimpleEntry or any other type applicable.

You're essentially looking for something like C#'s anonymous types i.e. you could just map to

new { k1 = entry.getKey(), k2 = e.getKey(), k3 = e.getValue()) }

and then immediately access those in the groupingBy and toMap phase.

Java has something similar but not quite i.e. you could do:

Map<Product, Map<Center, Value>> result = 
           given.entrySet()
                .stream()
                .flatMap(entry -> entry.getValue()
                        .entrySet().stream()
                        .map(e -> new Object() {
                            Center c = entry.getKey();
                            Product p = e.getKey();
                            Value v = e.getValue();
                        }))
                .collect(Collectors.groupingBy(o -> o.p, Collectors.toMap(o -> o.c, o -> o.v))); 

credit goes to @shmosel.

The only benefit here being you don't need to predefine a custom class.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=85579&siteId=1
Map
Map
map
map