1

I have a list of objects List<WorkingActivityModel> mList = new ArrayList<>();

public class WorkingActivityModel {
   private String name;
   private Sales sales;
   private Shifts shifts;
}

The Sales model is:

public class Sales {
    private int product1;
    private int product2;
}

The Shifts model is:

public class Shifts {
    private int shift1;
    private int shift2;
}

How can I group items in List<WorkingActivity> mList by name and then sum corresponding sales and shifts? So if I have 2 items in the list with the same name then group them and sum its sales and shifts?

I have tried stream but with no success:

List<WorkingActivityModel> result = mList.stream()
                        .collect(groupingBy(WorkingActivityModel::getName, LinkedHashMap::new, toList()))
                        .values().stream()
                        .flatMap(Collection::stream)
                        .collect(toList());

Another option is:

List<WorkingActivity> mList = new ArrayList<>();

HashMap<String, List<WorkingActivityModel>> map = new HashMap<>();

for(WorkingActivityModel o : mList){
    if (!map.containsKey(o.getKey())) {
        List<WorkingActivityModel> list = new ArrayList<>();
        list.add(o);

        map.put(o.getKey(), list);
    } else {
        map.get(o.getKey()).add(o);
    }
}

In this case, I don't know how to sum corresponding sales and Shifts

Edit

below an example of the expected result:

enter image description here

Naman
  • 27,789
  • 26
  • 218
  • 353
Erjon
  • 923
  • 2
  • 7
  • 32

4 Answers4

3

You can do this:

 mList.stream()
            .collect(Collectors.toMap(WorkingActivityModel::getName,Function.identity(),
                  (o, o2) -> {
                       o.getSales().sum(o2.getSales());
                       o.getShifts().sum(o2.getShifts());
                       return o;
                   })
           )
           .values()

and:

public Sales sum(Sales s) {
    this.product1 += s.product1;
    this.product2 += s.product2;
    return this;
}

public Shifts sum(Shifts s) {
    this.shift1 += s.shift1;
    this.shift2 += s.shift2;
    return this;
}
Hadi J
  • 16,989
  • 4
  • 36
  • 62
  • This solution is doing the trick. Except the list is not grouping but to the list is added the sum of the models, so the list was with two items, with this solution is added another one which has the required result. But the problem is that i need only the item which has the sum. I don't know if I'm clear – Erjon Jul 15 '20 at 11:08
2

You can do that using a custom merge operation defined such that performing toMap becomes slightly easier.

Collection<WorkingActivityModel> result = mList.stream()
        .collect(Collectors.toMap(WorkingActivityModel::getName, Function.identity(),
                (a, b) -> WorkingActivityModel.mergeModels(a, b))) // WorkingActivityModel::mergeModels
        .values();

The mergeModels is a static method that I have defined within WorkingActivityModel class such as it deals with the logic of merging two similarly named models.

static WorkingActivityModel mergeModels(WorkingActivityModel model1, WorkingActivityModel model2) {
    return new WorkingActivityModel(model1.name,
            Sales.mergeSales(model1.sales, model2.sales),
            Shifts.mergeShifts(model1.shifts, model2.shifts));
}

Of course, the mergeSales and mergeShifts follow the same pattern and are trivial to implement.

Naman
  • 27,789
  • 26
  • 218
  • 353
  • Hello, very nice, following your instructions I managed to resolve it. I streamed the list like this `mList.stream().collect(Collectors.groupingBy(WorkingActivityModel::getKey, Collectors.reducing(WorkingActivityModel::mergeModels))).values().stream().filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());` Thank you very much. – Erjon Jul 15 '20 at 13:23
  • @Erjon Great. Then further you might want to learn [Java Streams: Replacing `groupingBy` and `reducing` by `toMap`](https://stackoverflow.com/questions/57041896/java-streams-replacing-groupingby-and-reducing-by-tomap) – Naman Jul 15 '20 at 13:28
1

You can also sum directly as item_added within a custom data structure.

import java.util.HashMap;

public class MyHash extends HashMap<String, WorkingActivityModel>
{

    @Override
    public WorkingActivityModel put(String key, WorkingActivityModel value)
    {
        if(this.containsKey(key))
        {
            WorkingActivityModel w = this.get(key);
            value.getSales().setProduct1(w.getSales().getProduct1() + value.getSales().getProduct1());
            value.getSales().setProduct2(w.getSales().getProduct2() + value.getSales().getProduct2());
        //  ... same 
        //  value.getShifts().setShift1(w.getShifts().getShift1() + value.getShifts().getShift1());
        //  value.getShifts().setShift1(w.getShifts().getShift2() + value.getShifts().getShift2());
            super.put(key, value);
        }
        else
        {
            super.put(key, value);
        }
        return value;
    }
    
    public String toString()
    {
        StringBuffer sb = new StringBuffer();
        this.forEach((k,v) -> sb.append(k+",sale_p1="+v.getSales().getProduct1()+",sale_p2="+v.getSales().getProduct2()+"\n"));
        return sb.toString();
    }
    
    public static void main(String args[])
    {
        Sales s1 = new Sales();
        s1.setProduct1(1);
        Sales s2 = new Sales();
        s2.setProduct1(2);
        s2.setProduct2(3);
        MyHash mh = new MyHash();
        
        WorkingActivityModel wam = new WorkingActivityModel();
        wam.setSales(s1);
        wam.setName("n1");
        mh.put(wam.getName(), wam);
        System.out.println(mh);
        
        WorkingActivityModel wam2 = new WorkingActivityModel();
        wam2.setSales(s2);
        wam2.setName("n1");
        mh.put(wam2.getName(), wam2);
        
        System.out.println(mh);
    }
}

...

//same with other class
public class Sales {
    public Sales()
    {
        this.product1 = 0;
        this.product2 = 0;
    }
    public int getProduct1() {
        return product1;
    }
    public void setProduct1(int product1) {
        this.product1 = product1;
    }
    public int getProduct2() {
        return product2;
    }
    public void setProduct2(int product2) {
        this.product2 = product2;
    }
    private int product1;
    private int product2;
}

Output

n1,sale_p1=1,sale_p2=0
n1,sale_p1=3,sale_p2=3
Traian GEICU
  • 1,750
  • 3
  • 14
  • 26
1

If my understanding of your question is right, this might help -

List<WorkingActivityModel> mList = generateList(6);

Map<String, WorkingActivityModel> map = mList.stream()
            .collect(groupingBy(WorkingActivityModel::getName))
            .entrySet().stream()
            .collect(Collectors.toMap(entry ->entry.getKey(),
                    entry -> {
                WorkingActivityModel wam = new WorkingActivityModel();
                wam.shifts = new Shifts();
                wam.shifts.shift1 = entry.getValue()
                        .stream()
                        .map(v -> v.shifts.shift1)
                        .reduce(0, Integer::sum);;
                wam.shifts.shift2 = entry.getValue()
                        .stream()
                        .map(v -> v.shifts.shift2)
                        .reduce(0, Integer::sum);
                wam.sales = new Sales();
                wam.sales.product1 = entry.getValue()
                        .stream()
                        .map(v -> v.sales.product1)
                        .reduce(0, Integer::sum);
                wam.sales.product2 = entry.getValue()
                        .stream()
                        .map(v -> v.sales.product2)
                        .reduce(0, Integer::sum);
                return wam;
            }));

The map will have grouped WorkingActivities by names. The Key is the name, and value is a WorkingActiviyModel object with the sum of shifts and products.

shyam mohan
  • 61
  • 1
  • 4