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)

      

+3


source to share


3 answers


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)]

      

+3


source


you can do it in one chain, but i guess you won't get rid of the grouping.



list.stream()
            .collect(Collectors.groupingBy(a -> a.getGroupName(), Collectors.maxBy((A a1, A a2) -> a1.value - a2.value))).entrySet().stream()
            .map(v -> v.getValue().get()).collect(Collectors.toList());

      

+1


source


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));

      

0


source







All Articles