Somaiah Kumbera Somaiah Kumbera - 5 months ago 10
Java Question

Sum up fields based on another field in a stream

I have a List of objects that look like this:

{
value=500
category="GROCERY"
},
{
value=300
category="GROCERY"
},
{
value=100
category="FUEL"
},
{
value=300
category="SMALL APPLIANCE REPAIR"
},
{
value=200
category="FUEL"
}


I would like to transform that into a List of objects that looks like this:

{
value=800
category="GROCERY"
},
{
value=300
category="FUEL"
},
{
value=300
category="SMALL APPLIANCE REPAIR"
}


Basically add up all the values with the same category.

Should I be using flatMap? Reduce? I don't understand the nuances of these to figure it out.

Help?

EDIT:

There are close duplicates of this question:
Is there an aggregateBy method in the stream Java 8 api?
and
Sum attribute of object with Stream API

But in both cases, the end result is a map, not a list

The final solution I used, based on answers by @AndrewTobilko and @JBNizet was:

List<MyClass> myClassList = list.stream()
.collect(Collectors.groupingBy(YourClass::getCategory,
Collectors.summingInt(YourClass::getValue)))
.entrySet().stream().map(e -> new MyClass(e.getKey(), e.getValue()).collect(toList());

Answer

With static import of the Collectors class:

list.stream()
    .collect(groupingBy(YourClass::getCategory,
                        summingInt(YourClass::getValue)));

You will get a map Map<String, Integer>. Your class has to have getValue and getCategory methods to write method references, something like

public class YourClass {
    ...
    public String getCategory() { return category; }
    public int getValue() { return value; }
}