9

Lets say I have a class below with getters and setters but with only default constructor.

Note: I'm not allowed to change the structure of this class.

class Target {

    private String year;
    private String month;
    private String name;
    private double target;
    private double achieved;


    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }

    public String getMonth() {
        return month;
    }

    public void setMonth(String month) {
        this.month = month;
    }

    public String getName() {
        return name;
    }

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

    public double getTarget() {
        return target;
    }

    public void setTarget(double target) {
        this.target = target;
    }

    public double getAchieved() {
        return achieved;
    }

    public void setAchieved(double achieved) {
        this.achieved = achieved;
    }    
}

I have to add the Target and Achieved columns values based on year and Name.

Year Month Name Target Achieved
2018  8    Joey 50.00   10.00
2018  9    Joey 200.00  100.00
2018  9    Fred 200.00  150.00
2018  9    Fred 20.00   50.00

So the output would be:

Year Month Name Target  Achieved
2018  8    Joey 50.00   10.00
2018  9    Joey 200.00  100.00
2018  9    Fred 220.00  200.00

I've seen an example on how I could achieve something like this if I had a constructor that accepts parameters but I'm not so clear on the concept Group by and sum objects like in SQL with Java lambdas?:

How do I achieve this with just the default constructor to get same type List<Target> but with calculated values of multiple columns?

Sujal
  • 671
  • 1
  • 16
  • 34
  • 1
    hm... I have provided an answer, but now the real question is *which* month do you want to keep? the first/last in encounter order? Or the max one? – Eugene Sep 23 '18 at 10:56
  • @Eugene I just want to count total based on month for each year and name. I've updated the Question. Thank you – Sujal Sep 23 '18 at 11:28
  • so it seems from your examples that you need to group based on 3 things here: year, month and name – Eugene Sep 23 '18 at 11:38
  • @Eugene In this example yes. But what I'm looking for is a flexible solution where I could get output based on grouping (Name), (Name+Year), (Name+Year+Month) also. – Sujal Sep 23 '18 at 11:50
  • how about providing an example? – Eugene Sep 23 '18 at 11:56
  • 1
    so you want a dynamic grouping? Cant you simply create a method that accepts your List and a Function that will act as the mapping Function for the key in the `Collectors.toMap`? – Eugene Sep 23 '18 at 12:32

2 Answers2

9

It seems that you need to group based on three things: Year, Month and Name, so this could look like this:

Collection<Target> merged = yourListOfTargets
            .stream()
            .collect(Collectors.toMap(
                    t -> List.of(t.getYear(), t.getMonth(), t.getName()),
                    Function.identity(),
                    (left, right) -> {
                        left.setTarget(left.getTarget() + right.getTarget());
                        left.setAchieved(left.getAchieved() + right.getAchieved());
                        return left;
                    }))
            .values();

As Federico mentions in comments, this will alter your elements in the initial List. You might be OK with it, but if you are not, you need to replace Function.identity() with a copying Function that would create a new Target from an existing one.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • @FedericoPeraltaSchaffner I'm a bit lost... Im modifying what? :| – Eugene Sep 24 '18 at 20:42
  • 1
    @FedericoPeraltaSchaffner right. I will mention this in the answer, to be honest I did not think that would cause any issues for the OP, but it's a valid point, I admit – Eugene Sep 25 '18 at 08:22
0
public class Test8 {
  static class Target {

    String year;
    int target, achieved;
    String month;
    String name;

    public Target(String string, String string2, String name, int achieved, int target) {

      this.year = string;
      this.month = string2;
      this.target = target;
      this.achieved = achieved;
      this.name = name;
    }

    @Override
    public String toString() {
      return "[year=" + year + 
          ", month=" + month 
          + ", target=" + target 
          + ", achieved=" + achieved
          + ", name="
          + name + "]";
    }

  }// target

  public static void main(String[] args) {

    List<Target> list = Arrays.asList(

        new Target("1993", "9", "Protijayi", 1000, 40), 
        new Target("1993", "9", "Protijayi", 600, 400),
        new Target("1987", "11", "Soudipta", 320, 200),
        new Target("1987", "11", "Soudipta", 500, 900),
        new Target("1985", "9", "Tabu", 300, 200),
        new Target("1986", "9", "Tabu", 700, 200)

    );


        Map<List<String>, Target> map = list
        .stream()
        .collect(
            Collectors.groupingBy(
                ch -> List.of(ch.year, ch.month, ch.name), 
                Collectors.collectingAndThen(

            Collectors.reducing(
    (a, b) -> new Target(a.year, a.month, a.name, a.target + b.target,a.achieved + b.achieved)

            ), Optional::get)

        ));
    System.out.println(" MAP ");
    System.out.println(map);
    System.out.println(".................");
    System.out.println(" MAP.VALUES() ");
    System.out.println(map.values());

  }// main

}

/*
  MAP 
{[1993, 9, Protijayi]=[year=1993, month=9, target=1600, achieved=440, name=Protijayi],
 [1985, 9, Tabu]=[year=1985, month=9, target=200, achieved=300, name=Tabu],
  [1987, 11, Soudipta]=[year=1987, month=11, target=820, achieved=1100, name=Soudipta],
   [1986, 9, Tabu]=[year=1986, month=9, target=200, achieved=700, name=Tabu]}
.................
 MAP.VALUES() 
[[year=1993, month=9, target=1600, achieved=440, name=Protijayi], 
[year=1985, month=9, target=200, achieved=300, name=Tabu],
[year=1987, month=11, target=820, achieved=1100, name=Soudipta],
 [year=1986, month=9, target=200, achieved=700, name=Tabu]]
*/
Soudipta Dutta
  • 1,353
  • 1
  • 12
  • 7