Could you please help me to sort my Map
?
I have the following structure and I want to assemble apps under customers in a LinkedHashMap
in a reversed order by value.
[{
"CUSTOMER_1": {
"APP_1": "1"
},
"CUSTOMER_2": {
"APP_1": "2",
"APP_3": "7",
"APP_2": "3"
}
}]
I already have a lambda that assembles the map:
final Map<String, Map<String, Long>> organizationApps = taskHandles.stream().map(this::mapPayload)
.flatMap(e -> e.entrySet().stream())
.collect(groupingBy(Map.Entry::getKey, of(HashMap::new,
(r, t) -> t.getValue().forEach((k, v) -> r.merge(k, v, Long::sum)),
(r1, r2) -> {
r2.forEach((v1, v2) -> r1.merge(v1, v2, Long::sum));
return r1;
}))
);
What I need now is to replace organizationApps
values that are represented by HashMap
with a LinkedHashMap
and put the values into LinkedHashMap
in a proper order.
How can I do it in lambda?
UPD:
I can achieve the necessary output with the following piece of code:
final Map<String, Map<String, Long>> organizationApps = taskHandles.stream().map(this::mapPayload)
.flatMap(e -> e.entrySet().stream())
.collect(groupingBy(Map.Entry::getKey, of(HashMap::new,
(r, t) -> t.getValue().forEach((k, v) -> r.merge(k, v, Long::sum)),
(r1, r2) -> {
r2.forEach((v1, v2) -> r1.merge(v1, v2, Long::sum));
return r1;
}))
);
organizationApps.forEach((key, value) -> {
final Map<String, Long> sorted = value.entrySet()
.stream()
.sorted(reverseOrder(Map.Entry.comparingByValue()))
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2,
LinkedHashMap::new));
organizationApps.put(key, sorted);
});
How can I do it in one lambda?
You can not avoid collecting the values into the map, before you can create the LinkedHashMap
ordered by the resulting values, but you can specify the conversion step as finisher function of your custom collector:
Map<String, Map<String, Long>> organizationApps = input.stream()
.flatMap(e -> e.entrySet().stream())
.collect(groupingBy(Map.Entry::getKey, Collector.of(HashMap::new,
(r, t) -> t.getValue().forEach((k, v) -> r.merge(k, v, Long::sum)),
(r1, r2) -> {
r2.forEach((v1, v2) -> r1.merge(v1, v2, Long::sum));
return r1;
},
(Map<String, Long> m) -> m.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2,
LinkedHashMap::new))
)));
which would be equivalent to the following (Java 9+) code
Map<String, Map<String, Long>> organizationApps = input.stream()
.flatMap(m -> m.entrySet().stream())
.collect(groupingBy(Map.Entry::getKey, collectingAndThen(flatMapping(
t -> t.getValue().entrySet().stream(),
toMap(Map.Entry::getKey, Map.Entry::getValue, Long::sum)),
(Map<String, Long> m) -> m.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2,
LinkedHashMap::new))
)));
At the end of this answer is a Java 8 compatible implementation of the flatMapping
collector, but your custom collector does the job as well.