I am trying to use Java-8 lambdas to solve the following problem:
Given a List<Transaction>
, for each Category.minorCategory
I require the sum of Transaction.amount
per Category.minorCategory
and a Map
of Transaction.accountNumber
with the sum of Transaction.amount
per Transaction.accountNumber
. I have this working, as per the code below.
I now have a requirement to group by Category.majorCategory
, essentially returning a Map<String, Map<String, MinorCategorySummary>>
keyed on Category.majorCategory
.
I have everything working up until the stage of grouping by Category.majorCategory
but struggle to see the solution; the paradigm shift of programming with lambdas is proving a steep learning curve.
TransactionBreakdown
is where the action happens and where I'd like to return a Map<String, Map<String, MinorCategorySummary>>
.
public class Transaction {
private final String accountNumber;
private final BigDecimal amount;
private final Category category;
}
public class Category {
private final String majorCategory;
private final String minorCategory;
}
public class MinorCategorySummary {
private final BigDecimal sumOfAmountPerMinorCategory;
private final Map<String, BigDecimal> accountNumberSumOfAmountMap;
private final Category category;
}
public class TransactionBreakdown {
Function<Entry<String, List<Transaction>>, MinorCategorySummary> transactionSummariser = new TransactionSummariser();
public Map<Object, MinorCategorySummary> getTransactionSummaries(List<Transaction> transactions) {
return transactions
.stream()
.collect(groupingBy(t -> t.getCategory().getMinorCategory()))
.entrySet()
.stream()
.collect(
toMap(Entry::getKey,
transactionSummariser));
}
}
public class TransactionSummariser implements Function<Entry<String, List<Transaction>>, MinorCategorySummary> {
@Override
public MinorCategorySummary apply(
Entry<String, List<Transaction>> entry) {
return new MinorCategorySummary(
entry.getValue()
.stream()
.map(Transaction::getAmount)
.collect(reducing(BigDecimal.ZERO, BigDecimal::add)),
entry.getValue()
.stream()
.collect(
groupingBy(Transaction::getAccountNumber,
mapping(Transaction::getAmount,
reducing(BigDecimal.ZERO, BigDecimal::add)))),
entry.getValue().get(0).getCategory());
}
}
Your class design seems odd to me. Why put category into the summary class only to then have the category as a map key? It would make more sense to have a summary class without category in it:
Now your two problems are solved clearly and with no redundancy: