Let Java group & order & top in call chain?
I have a POJO class
class A {
public int id;
public String groupName;
public String getGroupName() { return this.groupName; }
public int value;
public A(int id, String groupName, int value) {
this.id = id;
this.groupName = groupName;
this.value = value;
}
}
And the id is unique, but the groupName is not. Then I have list A.
List<A> list = new ArrayList<A>();
list.add(new A(1, "A", 3));
list.add(new A(2, "B", 5));
list.add(new A(3, "B", 7));
list.add(new A(4, "C", 7));
I want to filter the list by groupName and value, return the largest value eachName.
List<B> filtedList = list....
//filtedList contain
//A(1, 'A', 3) A(3, 'B', 7) A(4, 'C', 7)
I knew I could write like
Map<String, List<A>> map = list.stream().collect(
Collectors.groupingBy(A::getGroupName)
);
List<A> result = new ArrayList<A>();
map.forEach(
(s, a) -> {
result.addAll(
deliveryOrderItems.stream().sorted(
(o1, o2) -> o2.value.compareTo(o1.value)
).limit(1).collect(Collectors.toList())
);
}
);
And the question is, can I remove the middle card and they work in one chained call Like
//list.stream().groupBy(A::getGroupName).orderInGroup(A::value).topInGroup(1)
source to share
What you can do is use groupingBy
with a follow-up collector.
In your case, maxBy
will do the job for you. This will give you Map<String, Optional<A>>
where each key is mapped to the complementary highest value according to the comparator you supplied.
Then you get the map values, filter them so that you only get non-empty options (avoiding using NSEE when calling get()
on Optional
). You finally retrieve the content you are collecting into List
.
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.maxBy;
import static java.util.stream.Collectors.toList;
...
List<A> resultList =
list.stream()
.collect(groupingBy(A::getGroupName,
maxBy(comparingInt(A::getValue))))
.values()
.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList());
Given your example, it outputs:
[A(1, A, 3), A(3, B, 7), A(4, C, 7)]
source to share
Alternatively, you can do this by creating just one thread:
Collector<A, ?, Map<String, A>> groupingBy = groupingBy(
A::getGroupName,
collectingAndThen(maxBy(comparingInt(A::getValue)),
Optional::get));
Collection<A> resultList = list.stream().collect(collectingAndThen(
groupingBy, Map::values));
source to share