Paweł Bąk
Paweł Bąk

Reputation: 33

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

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?

Upvotes: 3

Views: 466

Answers (2)

Ousmane D.
Ousmane D.

Reputation: 56433

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.

Upvotes: 3

shmosel
shmosel

Reputation: 50716

Some things are easier done without streams:

Map<Product, Map<Center, Value>> result = new HashMap<>();
given.forEach((c, pv) -> pv.forEach((p, v) ->
        result.computeIfAbsent(p, k -> new HashMap<>()).put(c, v)));

Upvotes: 7

Related Questions