Jan Jaff
Jan Jaff

Reputation: 5

How to stream and collect multiple max items based on a specific fields

List<Car> carList = new ArrayList<>();
Car car = new Car();
car.setMake("Honda");
car.setYear(1998);
car.setColor("red");
carList.add(car);

car = new Car();
car.setMake("Honda");
car.setYear(2020);
car.setColor("red");
carList.add(car);

car = new Car();
car.setMake("Audi");
car.setYear(2022);
car.setColor("red");
carList.add(car);

car = new Car();
car.setMake("Toyota");
car.setYear(2021);
car.setColor("blue");
carList.add(car);

car = new Car();
car.setMake("Toyota");
car.setYear(2007);
car.setColor("blue");
carList.add(car);

How to stream and collect to a map with color as key and value has list of cars with oldest car by make?

The final Map should be:

{
  "red": [
    {
      "make": "Honda",
      "year": "1998"
    },
    {
      "make": "Audi",
      "year": "2022"
    }
  ],
  "blue": [
    {
      "make": "Toyota",
      "year": "2007"
    }
  ]
}

Once the groupingBy is used to group based on color, having issue to reduce the list (values) to oldest car by make and then collect to a map as whole.

I was able to get the expected result by :

Map<String, Map<String, Optional<Car>>> carMap =
                carList.stream().collect(Collectors.groupingBy(Car::getColor,
                        Collectors.groupingBy(Car::getMake,
                                Collectors.minBy(Comparator.comparing(Car::getYear)))));
{red={Audi=Optional[Car(make=Audi, year=2022, color=red)], Honda=Optional[Car(make=Honda, year=1998, color=red)]}, blue={Toyota=Optional[Car(make=Toyota, year=2007, color=blue)]}}

But unable to flat map the values in the nested map to a list of Cars in the parent map.

Upvotes: 0

Views: 684

Answers (1)

Mihe
Mihe

Reputation: 2318

Basically, you should just have to map the collected values:

cars.stream().collect(Collectors.groupingBy(Car::color,
    Collectors.collectingAndThen(
        Collectors.groupingBy(Car::make,
            Collectors.collectingAndThen(
                Collectors.minBy(Comparator.comparing(Car::year)), Optional::get)
        ), map -> new ArrayList<>(map.values()))));

But really, this is where streams get unreadable...

Update

To write this in at least a little more readable fashion:

Collector<Car, ?, Car> eldest = Collectors.collectingAndThen(
        Collectors.minBy(Comparator.comparing(Car::year)),
        Optional::get);

Collector<Car, ?, List<Car>> eldestByMake = Collectors.collectingAndThen(
        Collectors.groupingBy(Car::make, eldest), 
        map -> new ArrayList<>(map.values()));

cars.stream().collect(Collectors.groupingBy(Car::color, eldestByMake));

Upvotes: 1

Related Questions