Home > Mobile >  Java streams - Get max two objects depending on condition
Java streams - Get max two objects depending on condition

Time:06-15

Relatively new to java streams and I have encountered an issue which I could not find a solution for.

I have A list of 10 objects and filtered them to return 4 objects.

List<MyObject> objects.stream()
                      .filter(x -> x.getFlag == 1)
                      .filter(x -> x.amount != null)
                      .collect(Collectors.toList());

this returns 4 objects:

OBJECT 1: {stageNumber = 2, stageToCalc = 1}
OBJECT 2: {stageNumber = 5, stageToCalc = 1}
OBJECT 3: {stageNumber = 9, stageToCalc = 7}
OBJECT 4: {stageNumber = 10, stageToCalc = 7}

So basically what I am trying to do is take stages that have the same value of stageToCalc and find the maximium stageNumber.

E.G: object 3 and object 4 have the same stageToCalc = 7, maximum value according to stage number should return Object 4: {stageNumber = 10, stageToCalc = 7} which is the higher value.

however, my issue comes when I need to get 2 (or more since it might be dynamic). in this case :

it should return object 2 and Object 4.

I have tried using:

objects.stream()
       .collect(groupingBy(Function.identity(), TreeMap::new, toList())
       .lastEntry()
       .getValue()
       .forEach(System.out::println)

This will just result in a classCastException, and if I Use .max() it would not compile. I could easy accomplish this in an expensive way with some for loops etc.

however I wonder if there is a way in using just 1 stream iteration.

Hope I am clear on this question. Still new and learning.

THANKS.

CodePudding user response:

You could try using Collectors.toMap():

public record MyObject(int stageNumber, int stageToCalc) {}


List<MyObject> objects = List.of(
        new MyObject(1, 1),
        new MyObject(5, 1),
        new MyObject(3, 1),
        new MyObject(1, 2),
        new MyObject(5, 3),
        new MyObject(1, 3)
);

Map<Integer, MyObject> result = objects.stream()
        .collect(Collectors.toMap(
                MyObject::stageToCalc,
                Function.identity(),
                BinaryOperator.maxBy(Comparator.comparing(MyObject::stageNumber))
        ));


System.out.println(result.values());

Output:

[MyObject[stageNumber=5, stageToCalc=1], MyObject[stageNumber=1, stageToCalc=2], MyObject[stageNumber=5, stageToCalc=3]]

CodePudding user response:

You can use Collectors.groupingBy() with a downstream Collectors.maxBy() to get the appropriate objects (With Collectors.collectAndThen() to pull them out of the Optional values returned by maxBy()):

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class Demo {
    private record MyObject(int stageNumber, int stageToCalc) {}


    public static void main(String[] args) {
        List<MyObject> objects = List.of(new MyObject(2, 1), new MyObject(5, 1), new MyObject(9, 7), new MyObject(10, 7));;

        List<MyObject>  maximums =
            new ArrayList<>(objects.stream()
                            .collect(Collectors.groupingBy(MyObject::stageToCalc,
                                                           Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(MyObject::stageNumber)),
                                                                                        Optional::orElseThrow)))
                            .values());
    System.out.println(maximums);
    }
}

will print out

[MyObject[stageNumber=5, stageToCalc=1], MyObject[stageNumber=10, stageToCalc=7]]

CodePudding user response:

Just do the grouping again, this time, for the other field:

record MyObject(int stageNumber, int stageToCalc) { }

List<MyObject> list = List.of(
        new MyObject(2, 1),
        new MyObject(5, 1),
        new MyObject(9, 7),
        new MyObject(10, 7)
);

list.stream()
.collect(Collectors.groupingBy(v -> v.stageToCalc, TreeMap::new, Collectors.toList()))
.lastEntry()
.getValue()
// ---------------------------------------
.stream()
.collect(Collectors.groupingBy(v -> v.stageNumber, TreeMap::new, Collectors.toList()))
.lastEntry()
.getValue()
// ---------------------------------------
.forEach(System.out::println);
  • Related