Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sum up fields based on another field in a stream [duplicate]

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());
like image 789
Somaiah Kumbera Avatar asked Mar 11 '23 19:03

Somaiah Kumbera


2 Answers

The Collectors class provides a 'groupingBy' that allows you to perform a 'group by' operation on a stream (similar behavior like GROUP BY in databases). Under the assumption that your list of objects is of type 'Objects', the following code should work:

Map<String, Integer> valueByCategory = myObjects.stream().collect(Collectors.groupingBy(MyObjects::getCategory, Collectors.summingInt(MyObjects::getValue)));

The code basically groups your stream by each category and runs a Collector on each group that sums up the return value of getValue() of every stream element. See https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html

like image 85
Christian Kuka Avatar answered Mar 15 '23 09:03

Christian Kuka


With static import of the Collectors class:

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

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

public class Class {
    private String category;
    private int value;

    public String getCategory() { return category; }
    public int getValue() { return value; }
}
like image 33
Andrew Tobilko Avatar answered Mar 15 '23 08:03

Andrew Tobilko