0

I am sitting in front of this simple problem and cannot get my head around...maybe its just too late :)
There is a simple "Food" class where a Food has id and amount (imagine shopping list):

public class Food {

    private String id;

    private Integer amount;

}

There is a list of foods that contains one "apple" and another list of foods that contains an "apple" and an "orange". The method addFood should process both lists using stream api and return a list that contains two "apples" and one orange:

List<Food> existingFood = new ArrayList<Food>();
existingFood.add(new Food("apple", 1));

List<Food> foodToAdd = new ArrayList<Food>();
foodToAdd.add(new Food("apple", 1));
foodToAdd.add(new Food("orange", 1));

List<Food> result = addFood(existingFood, foodToAdd);
        
// result should contain:
// 1. {"apple", 2}
// 2. {"orange", 1} 

Whats the most elegant way to do that using stream api? Thanks for your help!

selmlin
  • 21
  • 5

1 Answers1

0

You can use Collectors.groupingBy with Collectors.summingInt:

List<Food> result = Stream.of(existingFood, foodToAdd)
        .flatMap(List::stream)
        .collect(Collectors.groupingBy(Food::getId, Collectors.summingInt(Food::getAmount)))
        .entrySet().stream()
        .map(e -> new Food(e.getKey(), e.getValue().intValue()))
        .collect(Collectors.toList());

Outputs

Food(id=orange, amount=1)
Food(id=apple, amount=2)

Note: the id in the DTOs generally be an Integer or Long, in you case the case the id can be another name, for example name

Youcef LAIDANI
  • 55,661
  • 15
  • 90
  • 140
  • Thanks for the reply, this does not exactly what I intended to achieve, I don't want a simple counting of the objects based on id, rather the counting of the amount attribute of the objects. If there would be 5 apples to add `foodToAdd.add(new Food("apple", 5));` the output result should contain an apple object with amount of 6. – selmlin Jun 28 '20 at 21:31
  • @selmlin you can change to `.collect(Collectors.groupingBy(Food::getId, Collectors.summingInt(Food::getAmount)))` check my edit – Youcef LAIDANI Jun 28 '20 at 21:33
  • 1
    Yes, awesome thank you! That's what I was looking for! Actually, I have BigDecimal instead of Integer in my real world problem, but `Collectors.reducing(BigDecimal.ZERO, Food::getAmount, BigDecimal::add)`also works with your solution and BigDecimal. – selmlin Jun 28 '20 at 21:47
  • @selmlin it would be better to simplify the combination of `groupingBy` and `reducing` and instead make use of `toMap(Food::getId, Food::getAmount, BigDecimal::add)`. See [this link](https://stackoverflow.com/questions/57041896/java-streams-replacing-groupingby-and-reducing-by-tomap) for more details. – Naman Jun 29 '20 at 01:12