I have two HashMaps in the form
Map<Integer, Map<String,Double>> MAP_1
Map<Integer, Map<String,Double>> MAP_2
Let INNER be the inner map, i.e.
Map<String,Double>
For each pair of corresponding keys, I want to perform an operation (e.g. subtraction) between the two inner maps. In turn, this operation should be performed between two corresponding keys of the inner maps. The resulting map should be
Map<Integer,List<Double> RESULT
Sample maps are
MAP_1
------------------------------
| | 2016-10-02 10.0 |
| ID1 | 2016-10-03 20.0 |
| | 2016-10-04 30.0 |
------------------------------
| | 2016-10-02 1.00 |
| ID2 | 2016-10-03 2.00 |
| | 2016-10-04 3.00 |
------------------------------
MAP_2
------------------------------
| | 2016-10-02 2.00 |
| ID1 | 2016-10-03 3.00 |
| | |
------------------------------
| | 2016-10-02 1.00 |
| ID3 | 2016-10-03 2.00 |
| | 2016-10-04 3.00 |
------------------------------
RESULT
---------------
| | 8.00 |
| ID1 | 17.0 |
| | |
---------------
So, regarding the outer maps, only ID1 has to be taken into account; in turn, the operation has to involve the inner (common) keys '2016-10-02' and '2016-10-03'.
Here my current code
Set<Integer> common_keys = new LinkedHashSet<Integer>(map1.keySet());
common_keys.retainAll(map2.keySet());
System.out.println("Common keys: "+common_keys.toString());
map1.forEach((k,v) -> {
Map<String,Double> submap_1 = new LinkedHashMap<String,Double>(v);
Map<String,Double> submap_2 = new LinkedHashMap<String,Double>(map2.get(k));
Set<String> commons = new LinkedHashSet<String>(submap_1.keySet());
commons.retainAll(submap_2.keySet());
System.out.println(k+" : common days: "+commons);
List<Double> val1 = submap_1.keySet()
.stream()
.filter(c -> commons.contains(c))
.map(c -> submap_1.get(c))
.collect(Collectors.toList());
List<Double> val2 = submap_2.keySet()
.stream()
.filter(c -> commons.contains(c))
.map(c -> submap_2.get(c))
.collect(Collectors.toList());
List<Double> ABS = IntStream.range(0, val1.size())
.mapToObj(i -> val1.get(i) - val2.get(i))
.collect(Collectors.toList());
diff_abs.put(k, ABS);
});
Is there a simpler and smart way to do this, by using JAVA 8' STREAM API?
Thanks in advance
Creating the Set is unnecessary. Replace this with a check for null in for the same key in the other map. Furthermore creating copies of the maps is unnecessary, since the maps are never modified in the method.
public static <T, U> Map<T, List<Double>> merge(Map<T, Map<U, Double>> m1, Map<T, Map<U, Double>> m2) {
Map<T, List<Double>> result = new HashMap<>();
m1.forEach((k, v) -> {
Map<U, Double> v2 = m2.get(k);
if (v2 != null) {
// both outer maps contain the same key
ArrayList<Double> list = new ArrayList<>();
v.forEach((innerK, innerV) -> {
Double d = v2.get(innerK);
if (d != null) {
// matching key in both inner maps
list.add(innerV - d);
}
});
// list.trimToSize();
result.put(k, list);
}
});
return result;
}
You can create a re-usable method for merging two maps by retaining common keys only and applying a function to both values:
public static <K,V,R> Map<K, R> merge(Map<K,V> map1, Map<K,V> map2, BiFunction<V,V,R> f) {
boolean hasOrder=map1.entrySet().spliterator().hasCharacteristics(Spliterator.ORDERED);
return map1.entrySet().stream()
.collect(hasOrder? LinkedHashMap<K,R>::new: HashMap<K,R>::new, (m,e)-> {
V v2 = map2.get(e.getKey());
if(v2!=null) m.put(e.getKey(), f.apply(e.getValue(), v2));
}, Map::putAll);
}
Then, you can easily implement the merging of nested maps by using the method twice:
public static <K1, K2> Map<K1, List<Double>> merge(
Map<K1, Map<K2, Double>> map1, Map<K1, Map<K2, Double>> map2) {
return merge(map1, map2,
(a,b) -> new ArrayList<>(merge(a, b, (x,y) -> x-y).values()));
}
Note that the code above is smart enough to create a LinkedHashMap if, and only if, the first map has an intrinsic order, e.g. is itself a LinkedHashMap or SortedMap, and a plain HashMap otherwise. The order of the second map is not retained anyway, which is not an issue if, as your original code seems to assume, both maps have the same order. If their order differs, there’s obviously no way to retain both orders.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With