Home > Software design >  How to Sum up the attribute values of objects in a list having particular IDs and assign it to anoth
How to Sum up the attribute values of objects in a list having particular IDs and assign it to anoth

Time:05-06

My classes.

class MyLoan {
    private Long loanId;
    private BigDecimal loanAmount;
    private BigDecimal totalPaid;
    ....
}
class Customer {
    private Long loanId;
    private List<MyLoan> myLoan;
}

I want to iterate over the myLoan from a Customer and calculate the totalPaid amount.

My logic is "If loanId is 23491L or 23492L, then add the loanAmount of those two loanId's and set the value in the totalPaid amount of loanId 23490L".totalPaid amount is always showing as zero with my logic below.

And want to use Java 8 streams, but unable to write multiple conditions when using streams.

BigDecimal spreadAmount;
for (MyLoan myloan: customer.getMyLoan()) {
    if (myloan.getLoanId() == 23491L || myloan.getLoanId() == 23492L) {
        spreadAmount = spreadAmount.add(myloan.getLoanAmount());
    }
    if (myloan.getLoanId() == 23490L) {
        myloan.setTotalPaid(spreadAmount);
    }
}

CodePudding user response:

The totalPaid field is not modified because your MyLoan instance with id 23490l is encountered before the other two MyLoans.

As @Silvio Mayolo has suggested in the comments you should first compute the total amount with a temp variable and then assign it to the totalPaid field of the MyLoan instance with id 23490l.

This is a stream implementation of what you were trying to do:

//If to make sure that the element MyLoan invoking the setter is actually present
if (myLoan.stream().map(MyLoan::getLoanId).anyMatch(value -> value == 23490l)){
    myLoan.stream()
            .filter(loan -> loan.getLoanId() == 23490l)
            .findFirst()
            .get()
            .setTotalPaid(myLoan.stream()
                    .filter(loan -> loan.getLoanId() == 23491l || loan.getLoanId() == 23492l)
                    .map(MyLoan::getLoanAmount)
                    .reduce(BigDecimal.valueOf(0), (a, b) -> a = a.add(b)));
}

WARNING

The aggregate operation get() could throw a NoSuchElementException if a MyLoan with id 23490l is not present within the list. This code must be used carefully by making sure that such element is present or at least handled in a try-catch block.

CodePudding user response:

Firstly, you need to fetch a loan for which you want to define a total paid amount. If this step succeeds, then calculate a total.

In order to find a loan with a particular id using streams, you need to create a stream over the customers loans and apply filter() in conjunction with findFirst() on it. It'll give you the first element from the stream that matches the predicate passed into the filter. Because result might not be present in the stream, findFirst() returns an Optional object.

Optional class offers a wide range of method to interact with it like orElse(), ifPresent(), orElse(), etc. Avoid blindly using get(), unless you didn't check that value is present, which is in many cases isn't the most convenient way to deal with it. Like in the code below, ifPresent() is being used to proceed with the logic if value is present.

So if the required loan was found, the next step is to calculate the total. Which is done by filtering out target ids, extracting amount by applying map() and adding the amounts together using reduce() as a terminal operation.

public static void setTotalPaid(Customer customer, Long idToSet, Long... idsToSumUp) {
    List<MyLoan> loans = customer.getMyLoan();
    getLoanById(loans, idToSet).ifPresent(loan -> loan.setTotalPaid(getTotalPaid(loans, idsToSumUp)));
}

public static Optional<MyLoan> getLoanById(List<MyLoan> loans, Long id) {
    return loans.stream()
        .filter(loan -> loan.getLoanId().equals(id))
        .findFirst();
}

public static BigDecimal getTotalPaid(List<MyLoan> loans, Long... ids) {
    Set<Long> targetLoans = Set.of(ids); // wrapping with set to improve performance
    
    return loans.stream()
        .filter(loan -> targetLoans.contains(loan.getLoanId()))
        .map(MyLoan::getLoanAmount)
        .reduce(BigDecimal.ZERO, BigDecimal::add);
}
  • Related