Search code examples
javalistjava-8java-streampojo

Stream of a nested POJOs List to a another POJO LIST


hope someone can help me. I have a POJO with the following structure:

public class Invoice{

private String docNum;
private String customer;
private ArrayList<InvoiceDetails> invoiceDetails;

/* Getters and setters*/
}

And another POJO with the following

public class InvoiceDetails{

private String vatGroup;
private Double vatAmount;
private Double amount;

/* Getters and setters*/
}

Besides I have a third one with the following

public class VatType{

private String vatGroup;
private Double vatAmount;
private Double amount;

/* Getters and setters*/
}

What I'm trying to do is to reduce a List of Invoiceto a Listof VatType grouped by vatGroup. Like de DISTINCT clause in SQL. Let's say I have the following List:

InvoiceDetails idA1 = new InvoiceDetails("S1", 100.0, 40.0);
InvoiceDetails idA2 = new InvoiceDetails("S2", 140.0, 40.0);
InvoiceDetails idA3 = new InvoiceDetails("A1", 50.0, 10.0);
ArrayList<InvoiceDetails> listA = new ArrayList<>();
listA.add(idA1);
listA.add(idA2);
listA.add(idA3);

Invoice invoiceA = new Invoice();
invoiceA.setDetailList(listA);

InvoiceDetails idB1 = new InvoiceDetails("S1", 200.0, 50.0);
InvoiceDetails idB2 = new InvoiceDetails("S2", 240.0, 50.0);
InvoiceDetails idB2 = new InvoiceDetails("A1", 100.0, 20.0);
ArrayList<InvoiceDetails> listB = new ArrayList<>();
listB.add(idB1);
listB.add(idB2);
listB.add(idB3);


Invoice invoiceB = new Invoice();
invoiceB.setDetailList(listB);

List<Invoice> invoiceList = new ArrayList<>();
invoiceList.add(invoiceA);
invoiceList.add(invoiceB);

The expected result would be a Listof VatTypewith the following items:

("S1",300.0,90.0)
("S2",380.0,90.0)
("A1",150.0,30.0)

How can I get this list using a streamin one shot. Avoiding to iterate through the Lists? Thanks in advance


Solution

  • You will first need a flatMap to create a stream of all InvoiceDetails in all Invoice in your list. After that you can reduce with the variant of toMap that gets a merge method. Finally values() method of Map will get a Collection of VatType:

    Collection<VatType> values = invoiceList.stream()
            .flatMap(invoices -> invoices.getInvoiceDetails().stream())
            .collect(toMap(InvoiceDetails::getVatGroup, VatType::new, (i1, i2) -> {
                i1.setAmount(i1.getAmount() + i2.getAmount());
                i1.setVatAmount(i1.getVatAmount() + i2.getVatAmount());
                return i1;
            }))
            .values();
    

    Where this VatType constructor is used:

    VatType(InvoiceDetails invoiceDetails) {
        vatGroup = invoiceDetails.getVatGroup();
        vatAmount = invoiceDetails.getVatAmount();
        amount = invoiceDetails.getAmount();
    }
    

    You can easily make a List from a Collection if you need one:

    List<VatType> vatTypes = new ArrayList<>(values);