1

I have list having fields Name, Currency and Amount. I want to group it by Name, Currency and Sum of Amount.

This is sample data and output

List<myPojo> rows= ................

public class myPojo
{

    private String name;
    private String currency;
    private BigDecimal amount;

......................
}

I want result/output in list

Shivprsad Sammbhare
  • 410
  • 3
  • 7
  • 20

2 Answers2

2

You can use Stream API. Use Collectors.toMap and use AbstractMap.SimpleEntry as key of map. Then define merge function for multiple values of the same key.

List<myPojo> res = new ArrayList<>(rows.stream()
                .collect(Collectors.toMap(
                        e -> new AbstractMap.SimpleEntry<>(e.getName(), e.getCurrency()),
                        Function.identity(),
                        (a, b) -> new myPojo(a.getName(), a.getCurrency(), a.getAmount().add(b.getAmount()))))
                .values());

Demo:

List<myPojo> list = new ArrayList<>();
list.add(new myPojo("A", "USD", new BigDecimal(1.0)));
list.add(new myPojo("A", "USD", new BigDecimal(2.0)));
list.add(new myPojo("A", "USD", new BigDecimal(3.0)));
list.add(new myPojo("B", "USD", new BigDecimal(1.0)));
list.add(new myPojo("B", "USD", new BigDecimal(2.0)));
list.add(new myPojo("B", "USD", new BigDecimal(3.0)));
list.add(new myPojo("A", "US", new BigDecimal(1.0)));
list.add(new myPojo("A", "US", new BigDecimal(2.0)));
list.add(new myPojo("A", "US", new BigDecimal(3.0)));
List<myPojo> res = new ArrayList<>(list.stream()
            .collect(Collectors.toMap(
                    e -> new AbstractMap.SimpleEntry<>(e.getName(), e.getCurrency()),
                    Function.identity(),
                    (a, b) -> new myPojo(a.getName(), a.getCurrency(), a.getAmount().add(b.getAmount()))))
            .values());
System.out.println(res.toString());

Output:

[myPojo [name=B, currency=USD, amount=6], 
 myPojo [name=A, currency=USD, amount=6], 
 myPojo [name=A, currency=US, amount=6]]

Note: Try to capitalize the name of the class like MyPojo for better convention

Eklavya
  • 17,618
  • 4
  • 28
  • 57
  • What if myPojo don't have constructor. Then how to get these values – Shivprsad Sammbhare Jul 21 '20 at 12:29
  • Create a constructor then, without constructor how you set values ? Without constructor, you need to set each value using setter. like `(a,b) -> {myPojo p = new myPojo(); p.setName(a.getName());..... return p;` – Eklavya Jul 21 '20 at 12:33
  • Another way is `(a, b) -> { a.setAmount(a.getAmount().add(b.getAmount())); return a;}))` but it will mulate your current list – Eklavya Jul 21 '20 at 12:43
  • @ShivprsadSammbhare Is your problem solved? feel free to ask for help – Eklavya Jul 21 '20 at 13:58
1

You could use groupingBy to generate a Map<String,Map<String,BigDecimal>> corresponding to Map<Name,Map<Currency,sum of Amount>>

List<myPojo> rows = List.of(new myPojo("ABCD", "USD", new BigDecimal(20)),
                            new myPojo("XYZ",  "GBP", new BigDecimal(60)),
                            new myPojo("XYZ",  "THB", new BigDecimal(35)),
                            new myPojo("ABCD", "INR", new BigDecimal(90)),
                            new myPojo("ABCD", "USD", new BigDecimal(80)),
                            new myPojo("XYZ",  "THB", new BigDecimal(45)));
    
Map<String,Map<String,BigDecimal>> map = 
            rows.stream()
            .collect(Collectors.groupingBy(myPojo::getName,
                    Collectors.groupingBy(myPojo::getCurrency,
                            Collectors.reducing(BigDecimal.ZERO, myPojo::getAmount, BigDecimal::add))));
    System.out.println(map);

 // output: {XYZ={GBP=60, THB=80}, ABCD={USD=100, INR=90}}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Eritrean
  • 15,851
  • 3
  • 22
  • 28