Search code examples
javaalgorithmdesign-patternscollectionsjava-8

Iterating through list and summing up all amounts where types are the same


I need to add up all amounts in MonthlyExpenses list where types are equal.

In my code I am appending expenses and obligations to same list of MonthlyExpenses.

Here is my code:

List<MonthlyExpenses> monthlyExpenses =  new ArrayList<MonthlyExpenses>();
List<Expenses> expenses = getExpenses(borrower);
List<Obligations> obligations = getObligations(borrower);

if(expenses!=null){
    monthlyExpenses.addAll(createMonthlyExpenses(expenses));
}
if(obligations!=null){
   monthlyExpenses.addAll(createMonthlyObligations(obligations));
}

...
public class MonthlyExpenses {

  private String type = null;
  private BigDecimal amount = null;

  public String getType() {
    return type;
  }

  public void setType(String type) {
    this.type = type;
  }

  public BigDecimal getAmount() {
    return amount;
  }

  public void setAmount(BigDecimal amount) {
    this.amount = amount;
  }
}

The 1st IF statement, if executed returns:

class MonthlyExpenses{
    type: FOOD
    amount: 150.00
}, class MonthlyExpenses{
    type: UTILITIES
    amount: 250.00
}, class MonthlyExpenses{
    type: TRANSPORTATION
    amount: 350.00
}, class MonthlyExpenses{
    type: CHILD CARE
    amount: 450.00
}, class MonthlyExpenses{
    type: CREDIT CARDS
    amount: 878.00
}, class MonthlyExpenses{
    type: Other
    amount: 2888.64
}

The 2nd IF statement, if executed returns:

class MonthlyExpenses{
    type: AUTO LOANS
    amount: 200.00
}, class MonthlyExpenses{
    type: CREDIT CARDS
    amount: 300.00
}, class MonthlyExpenses{
    type: INSTALLMENT LOANS
    amount: 50.00
}, class MonthlyExpenses{
    type: ALIMONY/SUPPORT
    amount: 75.00
}, class MonthlyExpenses{
    type: Other
    amount: 10096.87
}

How do I check in List<MonthlyExpenses> if types are equal and sum up those amounts and return that new list?


Solution

  • You can achieve this by using streams and collectors:

        private static List<MonthlyExpense> createMonthlyExpenses(List<MonthlyExpense> expenses) {
            Map<String, Double> sums = expenses.stream()
                    .collect(Collectors.groupingBy(
                                MonthlyExpense::getType, 
                                Collectors.summingDouble(MonthlyExpense::getAmount))
            );
            return sums.entrySet().stream()
                    .map(entry -> new MonthlyExpense(entry.getKey(), entry.getValue()))
                    .sorted(Comparator.comparing(MonthlyExpense::getType))
                    .collect(Collectors.toList());
        }
    

    The way this work is by using collect on all expenses grouping them by (groupBy) their type and using as a summing downstream collector. Then, you use this map (between types and total amounts) to create the objects with total expenses that you require. The following class is a full working application of this in action, for you to play with:

    import java.util.*;
    import java.util.stream.Collectors;
    
    public class FinancialCalculator {
        
        static class MonthlyExpense {
            private String type;
            private double amount;
    
            public MonthlyExpense(String type, double amount) {
                this.type = type;
                this.amount = amount;
            }
            
            public String getType() { return type; }
            public double getAmount() { return amount; }
            public String toString() { return String.format("%s: %.2f", type, amount); }
        }
        
        private static List<MonthlyExpense> createMonthlyExpenses(List<MonthlyExpense> expenses) {
            Map<String, Double> sums = expenses.stream()
                    .collect(Collectors.groupingBy(
                                MonthlyExpense::getType, 
                                Collectors.summingDouble(MonthlyExpense::getAmount))
            );
            return sums.entrySet().stream()
                    .map(entry -> new MonthlyExpense(entry.getKey(), entry.getValue()))
                    .collect(Collectors.toList());
        }
        
        public static void main(String[] args) {
            MonthlyExpense[] expenses = new MonthlyExpense[] {
                    new MonthlyExpense("UTILITIES", 75),
                    new MonthlyExpense("CREDIT", 1000),
                    new MonthlyExpense("UTILITIES", 50),
                    new MonthlyExpense("CREDIT", 2000),
                    new MonthlyExpense("UTILITIES", 150),
                    new MonthlyExpense("CAR", 344)
            };  
            System.out.println(createMonthlyExpenses(Arrays.asList(expenses)));
        }
    }
    

    Complete code on GitHub

    Hope this helps.