I have a map Map<String, List<Double>
and I would like to find the maximum (or minimum) value in all of the lists.
The function should return the maximum (or minimum) value and the key belonging to that value.
The signature may be
public static Pair<String,Double> getKeyValue(Map<String, List<Double>> map, BinaryOperator<Double> function)
which gets the map and the function Double::max
or Double::min
How can i implement this efficiently (and beautifully) using java 8, stream api ?
A BinaryOperator
is not a good specification for that task, it is straight-forward to use in a reduction to produce the appropriate value, e.g. minimum or maximum, however it is not suitable for returning an associated value like the Map
’s key value. Using it this way implies that the implementation has to perform additional operations to find out, what the BinaryOperator
actually did in order to select the correct key value during the reduction. Even worse, it can’t guaranty that the BinaryOperator
does something that allows to perform this kind of reduction, e.g. the operator might return a value which is neither of its arguments.
For such a task, a Comparator
is the better choice as it is designed for specifying an ordering and perform related operations like finding maximum and minimum. An implementation might look like this:
public static Pair<String,Double> getMinimumKeyValue(
Map<String, List<Double>> map, Comparator<Double> function) {
return map.entrySet().stream()
.map(e->new Pair<>(e.getKey(), e.getValue().stream().min(function).get()))
.min(Comparator.comparing(Pair::getRight, function)).get();
}
It is named getMinimumKeyValue
as it will return the minimum key/value pair when you pass in Comparator.naturalOrder()
.
But you can get the maximum as well by passing Comparator.reverseOrder()
.
And it’s easy to modify to support a broader range of use cases:
public static <K,V> Pair<K,V> getMinKeyValue(
Map<K, ? extends Collection<V>> map, Comparator<? super V> function) {
return map.entrySet().stream()
.map(e->new Pair<>(e.getKey(), e.getValue().stream().min(function).get()))
.min(Comparator.comparing(Pair::getRight, function)).get();
}
This still works for getting a Pair<String,Double>
out of a Map<String, List<Double>>
but can do a lot more…
You can try this :
public static Pair<String,Double> getKeyValue(Map<String,List<Double>> map,
BinaryOperator<Double> function) {
return map.entrySet().stream()
.map(e -> new Pair<String,Double>(e.getKey(),e.getValue().stream().reduce(function).get()))
.reduce((p1,p2) -> function.apply(p1.getValue(),p2.getValue()).equals(p1.getValue()) ? p1 : p2)
.get();
}
Explanation :
First, you associate each Entry<String,List<Double>>
to a Pair<String,Double>
where the value is the min (or max) of the list and the key remains the same, then you compare each Pair<String,Double>
to find the one which has the lowest (or highest) value.
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