Search code examples
group-byjava-8java-stream

Java 8 group by sum condition


I have an object Officer

public class Officer {

    private String name;
    private int totalDaysInOffice;

    public Officer(String name, int totalDaysInOffice) {
        this.name = name;
        this.totalDaysInOffice = totalDaysInOffice;
    }

    @Override
    public String toString() {
        return "Officer{" +
                "name='" + name + '\'' +
                ", totalDaysInOffice=" + totalDaysInOffice +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getTotalDaysInOffice() {
        return totalDaysInOffice;
    }

    public void setTotalDaysInOffice(int totalDaysInOffice) {
        this.totalDaysInOffice = totalDaysInOffice;
    }
}

Here each officer have spent days in office(just made up variable).

What I want to do is the divide the officers once I have the sum of days 10000 in separate list

Based on example below , I want to have list with

one list with John , Matthew , and Robert since they sum to more 10K 
One list with Patrick as he has 10K 
Dave would be in separate list.

I have tried group by but not sure how can I add this condition.

public class OffierExample {

    public static void main(String[] args) {

        List<Officer> officerList = new ArrayList<>();

        officerList.add(new Officer("John",5000));
        officerList.add(new Officer("Matthew",3000));
        officerList.add(new Officer("Robert",2000));
        officerList.add(new Officer("Dave",2000));
        officerList.add(new Officer("Patrick",10000));

        Map<Officer, Integer> collect = officerList.stream().collect(Collectors.groupingBy(o -> o, Collectors.summingInt(Officer::getTotalDaysInOffice)));
        System.out.println(collect);
    }
}

Is there anyways it can be done in Java 8

**

*****UPDATE*****

**

I have achieved using traditional loop but I want to use Java 8 group by if possible

public class OffierExample {

    public static void main(String[] args) {

        List<Officer> officerList = new ArrayList<>();

        officerList.add(new Officer("John", 5000));
        officerList.add(new Officer("Matthew", 3000));
        officerList.add(new Officer("Robert", 2000));
        officerList.add(new Officer("Dave", 2000));
        officerList.add(new Officer("Patrick", 10000));

        officerList.add(new Officer("YYYY", 600));
        officerList.add(new Officer("XXXX", 600));

        //keep totalDaysInOfficeSum
        int totalDaysInOfficeSum = 0;

        //the final list
        List<List<Officer>> off = Lists.newArrayList();

        //the working list
        List<Officer> tempOffList = Lists.newArrayList();

        for (Officer officer : officerList) {

            //get sum
            totalDaysInOfficeSum = totalDaysInOfficeSum + officer.getTotalDaysInOffice();

            //if sum is more than 10K or equal
            if (totalDaysInOfficeSum >= 10000) {

                //add it in temp list
                tempOffList.add(officer);

                //add in master list
                off.add(tempOffList);

                //reset temp list
                tempOffList = new ArrayList<>();

                //reset sum
                totalDaysInOfficeSum = 0;
                continue;

            }

            //add in temp list
            tempOffList.add(officer);


        }

        //any left over
        if (!tempOffList.isEmpty()) {
            off.add(tempOffList);
        }

        //printint out
        System.out.println("Officers list =" + off.size());

        off.forEach(o -> {

            System.out.println("List size:" + o.size());
            o.forEach(oo -> {
                System.out.println(oo.getName() + "::" + oo.getTotalDaysInOffice());
            });
            System.out.println("====================");
        });
    }
}

Output

Officers list =3
List size:3
John::5000
Matthew::3000
Robert::2000
====================
List size:2
Dave::2000
Patrick::10000
====================
List size:2
YYYY::600
XXXX::600
====================

Solution

  • Something like this:

    List<List<Officer>> result = officerList.stream().collect(Collector.of(
                () -> new ArrayList<List<Officer>>(),
                (list, entry) -> {
                    if (list.size() == 0) {
                        List<Officer> inner = new ArrayList<>();
                        inner.add(entry);
                        list.add(inner);
                    } else {
                        List<Officer> last = list.get(list.size() - 1);
                        int sum = last.stream().mapToInt(Officer::getTotalDaysInOffice).sum();
                        if (sum < 10_000) {
                            last.add(entry);
                        } else {
                            List<Officer> inner = new ArrayList<>();
                            inner.add(entry);
                            list.add(inner);
                        }
                    }
                },
                (left, right) -> {
                    throw new IllegalArgumentException("Not for parallel");
                }));