Combine fields based on another field in the 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 convert this to a list of objects that looks like this:

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

Basically, all the values ​​with the same category are added up.

Should I use flatMap? Reduce? I do not understand its nuances to understand this.

reference

EDIT:

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

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

The last solution I used based on the answers of @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());
+4
3

Collectors:

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

Map<String, Integer>. Class getValue getCategory , -

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

    public String getCategory() { return category; }
    public int getValue() { return value; }
}
+2

Collectors "groupingBy", "group by" ( , GROUP BY ). , "", :

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

, getValue() . . https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html

+5

:

List<Obj> values = list.stream().collect(
        Collectors.groupingBy(Obj::getCategory, Collectors.reducing((a, b) -> new Obj(a.getValue() + b.getValue(), a.getCategory())))
).values().stream().map(Optional::get).collect(Collectors.toList());

- stream() Optional<Obj> Map<String, Optional<Obj>> .

( ) :

List<Obj> values2 = list.stream()
    .sorted((o1, o2) -> o1.getCategory().compareTo(o2.getCategory()))
    .collect(
        LinkedList<Obj>::new,
        (ll, obj) -> {
            Obj last = null;
            if(!ll.isEmpty()) {
                last = ll.getLast();
            }

            if (last == null || !last.getCategory().equals(obj.getCategory())) {
                ll.add(new Obj(obj.getValue(), obj.getCategory())); //deep copy here
            } else {
                last.setValue(last.getValue() + obj.getValue());
            }
        },
        (list1, list2) -> {
              //for parallel execution do a simple merge join here
              throw new RuntimeException("parallel evaluation not supported"); 
         }
    );

Obj , , .

, Java (. )

: https://ideone.com/p3bKV8

0

All Articles