Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map<X, Map<Y, Z> to Map<Y, Map<X, Z>

I want to switch keys of map and a map inside it:

Map<X, Map<Y, Z> -> Map<Y, Map<X, Z>

I've tried using streams, but can't create the inside map or how to access key and value from the original inside map separately.

//So far I've tried:

originalMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
like image 843
Ania Avatar asked Jul 16 '19 08:07

Ania


2 Answers

A bit complex, but it works:

Map<Y,Map<X,Z>> out =
      originalMap.entrySet()
                 .stream()
                 .flatMap(e -> e.getValue()
                                .entrySet()
                                .stream()
                                .map(e2 -> {
                                    Map<X,Z> m = new HashMap<>();
                                    m.put(e.getKey(),e2.getValue());
                                    return new SimpleEntry<>(e2.getKey(),m);}))
                 .collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,(v1,v2)->{v1.putAll(v2);return v1;}));

Basically, it transforms the Stream of the original Map's entries into a Stream of flat entries of the desired output Map (where each inner Map has just a single Entry), and uses a toMap collector with a merge function to merge the inner Maps that correspond with the same outer key.

For, example, running this code with the following input Map:

Map<String,Map<Integer,Double>> originalMap = new HashMap<>();
Map<Integer,Double> inner1 = new HashMap<>();
Map<Integer,Double> inner2 = new HashMap<>();
Map<Integer,Double> inner3 = new HashMap<>();
originalMap.put("aa",inner1);
originalMap.put("bb",inner2);
originalMap.put("cc",inner3);
inner1.put(10,10.0);
inner1.put(20,20.0);
inner1.put(30,30.0);
inner2.put(10,40.0);
inner2.put(20,50.0);
inner2.put(30,60.0);
inner3.put(10,70.0);
inner3.put(20,80.0);
inner3.put(30,90.0);

Map<Integer,Map<String,Double>> out =
    originalMap.entrySet()
               .stream()
               .flatMap(e -> e.getValue()
                              .entrySet()
                              .stream()
                              .map(e2 -> {
                                  Map<String,Double> m = new HashMap<>();
                                  m.put(e.getKey(),e2.getValue());
                                  return new SimpleEntry<>(e2.getKey(),m);}))
               .collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,(v1,v2)->{v1.putAll(v2);return v1;}));
System.out.println (out);

Will output:

{20={aa=20.0, bb=50.0, cc=80.0}, 10={aa=10.0, bb=40.0, cc=70.0}, 30={aa=30.0, bb=60.0, cc=90.0}}

EDIT:

If you are using Java 9 or higher, you can use Map.of to simplify the code a bit:

Map<Y,Map<X,Z>> out =
      originalMap.entrySet()
                 .stream()
                 .flatMap(e -> e.getValue()
                                .entrySet()
                                .stream()
                                .map(e2 -> new SimpleEntry<>(e2.getKey(),Map.of(e.getKey(),e2.getValue()))))
                 .collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,(v1,v2)->{v1.putAll(v2);return v1;}));
like image 159
Eran Avatar answered Nov 07 '22 14:11

Eran


To me, this is a lot easier without streams, actually; but still using java-8 features (the example is taken from the other answer):

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

    originalMap.forEach((key, value) -> {
        value.forEach((innerKey, innerValue) -> {

            Map<String, Double> map = new HashMap<>();
            map.put(key, innerValue);

            result.merge(innerKey, map, (left, right) -> {
                left.putAll(right);
                return left;
            });
        });
    });

    System.out.println(result);
like image 29
Eugene Avatar answered Nov 07 '22 15:11

Eugene