Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group List of Objects to Create an Extended Object List

I have these sample records from database which is a result of group by query:

[
 {
   "name": "Troy",
   "acct": 1123,
   "type": " Savings",
   "total": 50
 },
 {
   "name": "Larry",
   "acct": 4233,
   "type": " Savings",
   "total": 200
 },
 {
   "name": "Troy",
   "acct": 1123,
   "type": " Current",
   "total": 120
 },
 {
   "name": "Larry",
   "acct": 4233,
   "type": " Current",
   "total": 220
 }
]

Now, i need to create a report that looks like so:

[
 {
   "name": "Troy",
   "acct": 1123,
   "totalSavings": 50,
   "totalCurrent": 120
 },
 {
   "name": "Larry",
   "acct": 4233,
   "totalSavings": 200,
   "totalCurrent": 220
 }
]

.

public class DbTrans {
    private String name;
    private String acct;
    private String type;
    private double total;

// getters and setters
...
}

I have tried using some lambda techniques like the one below, but i'm still not getting close to the solution i desire.

 Map<String, List<DbTrans>> collect = transList.stream().collect(
          Collectors.groupingBy(f -> f.getType()));
like image 894
Olantobi Avatar asked Mar 03 '23 19:03

Olantobi


2 Answers

First of all the response Dto is not the same as the request Dto, what I suggest is to create a new Response class lests call it:

public class DbTransResponse {
    private String name;
    private String acct;
    private double totalSavings;
    private double totalCurrent;
    // getters and setters
}

then the result can be like so :

List<DbTransResponse> result = transList.stream()
        .collect(Collectors.groupingBy(DbTrans::getAcct))
        .values().stream()
        .map(trans -> new DbTransResponse(
                trans.get(0).getName(),
                trans.get(0).getAcct(),
                trans.get(0).getTotal(),
                trans.get(1).getTotal()
        )).collect(Collectors.toList());

I consider that the list should contain two entries of each name so the you can get totalSavings from the first entry trans.get(0).getTotal() and totalCurrent from the second trans.get(1).getTotal().

If you are not sure you can use conditions to fill your object for example you can check if there are two elements if not set a default value.


Ideone demo

Outputs

DbTransResponse{name='Larry', acct='4233', totalSavings=200.0, totalCurrent=220.0}
DbTransResponse{name='Troy', acct='1123', totalSavings=50.0, totalCurrent=120.0}
like image 80
YCF_L Avatar answered Mar 15 '23 11:03

YCF_L


you can use Collectors::toMap for this purpose (with a single collect operation)

 Map<Integer, DbTransResponse> collect = transList.stream()
            .collect(Collectors.toMap(DbTrans::getAcct, 
                                      DbTransResponse::new, 
                                      DbTransResponse::merge));

 Collection<DbTransResponse> result = collect.values();

here is merge method in DbTransResponse class

static DbTransResponse merge(DbTransResponse r1, DbTransResponse r2) {
        return new DbTransResponse(
                r1.name, r1.acct,
                r1.totalSavings + r2.totalSavings,
                r1.totalCurrent + r2.totalCurrent
        );
    }

and an additional constructor for DbTransResponse class, though you can move this logic to a method

DbTransResponse(DbTrans dbTrans) {
   this.name = dbTrans.getName();
   this.acct = dbTrans.getAcct();
   this.totalSavings = "Savings".equals(dbTrans.getType()) ? dbTrans.getTotal() : 0;
   this.totalCurrent = "Current".equals(dbTrans.getType()) ? dbTrans.getTotal() : 0;

}

demo

like image 35
Adrian Avatar answered Mar 15 '23 13:03

Adrian