Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Stream group by one attribute and collect max element by another attribute

I am wondering if there is already an implemented feature in streams (or Collectors) which first groups a stream by an attribute and then returns the first element in the list sorted by another attribute. E.g. the following code tries to group a stream of objects using the first attribute and then wants to collect that object which has the highest value of the second attribute.

class MyClass{
 String att1;
 String att2;
}

Now, I want to achieve something like this with Stream myClassStream -

Map<String,MyClass> myMap = myClassStream().collect(Collectors.groupingBy(MyClass::getAtt1)); //Now I want to do Sorting after grouping to collect only the element which has the highest value of attr2.

My code using simple for loop is:

Map<String, MyClass> postAnalyticsMap = new HashMap<>();
for (MyClass post : myClassList) {
      if (post.get(post.getAtt1()) == null) {
               post.put(post.getAtt1(), post);
      } else {
               MyClass existingClass = postAnalyticsMap.get(post.getAtt1());
               if (existingPostAnalytics.getAtt2() < post.getAtt2()) {
                    postAnalyticsMap.put(post.getAtt1(), post);
               }
        }
  }

Any help will be much appreciated.

like image 609
The-Proton-Resurgence Avatar asked Mar 14 '20 16:03

The-Proton-Resurgence


2 Answers

Use the toMap with merge function to find the max element on second attribute

Map<String, MyClass> map = myClassList.stream()
        .collect(Collectors.toMap(MyClass::getAtt1, Function.identity(),
                BinaryOperator.maxBy(Comparator.comparing(MyClass::getAtt2))));
like image 109
Deadpool Avatar answered Sep 21 '22 10:09

Deadpool


You can do something like this:

myClassStream()
     .collect(Collectors.groupingBy(MyClass::getAtt1,
            Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(MyClass::getAtt2)),
                            myClass -> myClass.map(MyClass::getAtt2).orElse(""))));
like image 24
Hadi J Avatar answered Sep 21 '22 10:09

Hadi J