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

Time:09-29

 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 have

{
  "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.

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 flatmap the values in the nested map to a list of Cars in the parent map.

CodePudding user response:

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