Switching Keys in a Map of Maps

Viewed 292

How would you use Java 8 streams to swap keys in this map of maps? Or at least clean up this mess a little bit...

    Map<Type1, Map<Type2, String>> to Map<Type2, Map<Type1, String>>

Using nested for loops (untested):

    Map<Type1, Map<Type2, String>> map ...
    Map<Type2, Map<Type1, String>> map2 = new HashMap<>();
        for (Type 1 type1 : map.keySet()) {
            for(Entry<Type2, String> entry : map.get(type1)) {
                if (map2.get(entry.key() == null) {
                    map2.push(entry.key(), new HashMap<Type1, String>();
             } 
             map2.get(entry.key()).put(type1, entry.value();
        }
    }

So far I think you would need to flap map into all unique combinations of Type1, Type2, and String and store this set in some sort of intermediate collection.

Definitely wrong:

map.entrySet().stream().flatMap(t -> <Type1, Type2, 
String>).collect(Collectors.toMap(t -> t.Type2, Collectors.toMap(t -> 
t.type1, t->t.String))
3 Answers

Streams aren't well-suited for this type of problem. Instead, consider using other java 8 additions -- Map#forEach and Map#computeIfAbsent:

map.forEach( (t1, e) -> 
    e.forEach( (t2, v) ->
            result.computeIfAbsent(t2, x -> new HashMap<>()).put(t1, v)
    )
);

Misha already showed you the straight forward solution. If you really want to use Streams it could look like this:

public static <S, T> Map<T, Map<S, String>> convertStream(Map<S, Map<T, String>> map) {
  return map.entrySet().stream().flatMap(m1 -> m1.getValue().entrySet()
                                                 .stream().map(e -> new Object() {
    final T outer = e.getKey();
    final Map<S, String> map;
    {
      map = new HashMap<>();
      map.put(m1.getKey(), e.getValue());
    }
  })).collect(Collectors.toMap(o -> o.outer, o -> o.map, (m1, m2) -> {
    m1.putAll(m2);
    return m1;
  }));
}
    Map<Type2, Map<Type1, Object>> finalAnswer = map.entrySet().stream()
.collect(()->new HashMap<Type2,Map<Type1,Object>>(),
            (mapAccumulator, left)->{               
                for(Entry<?, ?> leftEntry : left.getValue().entrySet() ){
                    Map<Type1,Object> tempMap = new HashMap<>();
                    tempMap.put(left.getKey(), leftEntry.getValue());
                    mapAccumulator.put((Type2) leftEntry.getKey(), tempMap);
                }

            /*accumulator*/},
        (mapLeft,mapRight)->{mapLeft.putAll(mapRight); /*combiner*/});

map.entrySet().forEach(System.out::println);
Related