Reputation: 9576
Given a List
of the following Transaction
class, using Java 8 lambdas, I want to obtain a List
of ResultantDTO
, one per account type.
public class Transaction {
private final BigDecimal amount;
private final String accountType;
private final String accountNumber;
}
public class ResultantDTO {
private final List<Transaction> transactionsForAccount;
public ResultantDTO(List<Transaction> transactionsForAccount){
this.transactionsForAccount = transactionsForAccount;
}
}
So far, I use the following code to group the List<Transaction>
by accountType
.
Map<String, List<Transaction>> transactionsGroupedByAccountType = transactions
.stream()
.collect(groupingBy(Transaction::getAccountType));
How do I return a List<ResultantDTO>
, passing the List from each map key into the constructor, containing one ResultantDTO
per accountType
?
Upvotes: 2
Views: 679
Reputation: 100209
You can do this in single stream operation:
public List<ResultantDTO> convert(List<Transaction> transactions) {
return transactions.stream().collect(
collectingAndThen(
groupingBy(
Transaction::getAccountType,
collectingAndThen(toList(), ResultantDTO::new)),
map -> new ArrayList<>(map.values())));
}
Here collectingAndThen
used twice: once for downstream Collector
to convert lists to the ResultantDTO
objects and once to convert the resulting map to list of its values.
Upvotes: 6
Reputation: 16067
You could use the toMap
collector:
Map<String, ResultantDTO> transactionsGroupedByAccountType = transactions
.stream()
.collect(toMap(Transaction::getAccountType,
t -> new ResultantDTO(t.getAmount(),
Stream.of(new SimpleEntry<>(t.getAccountNumber(), t.getAmount()))
.collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue))),
(dt1, dt2) -> new ResultantDTO(dt1.getSumOfAmountForAccountType().add(dt2.getSumOfAmountForAccountType()),
Stream.of(dt1.getAccountNumberToSumOfAmountForAccountNumberMap(), dt2.getAccountNumberToSumOfAmountForAccountNumberMap())
.flatMap(m -> m.entrySet().stream())
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue)))));
although its not very readable. Maybe you could add a constructor that takes a single Transaction
as parameter and a merger in the ResultantDTO
class:
public ResultantDTO(Transaction t) {
///
}
static ResultantDTO merger(ResultantDTO r1, ResultantDTO r2) {
///
}
and there it is more readable:
Map<String, ResultantDTO> transactionsGroupedByAccountType = transactions
.stream()
.collect(toMap(Transaction::getAccountType,
ResultantDTO::new,
ResultantDTO::merger));
Upvotes: 1
Reputation: 328608
Assuming you have a:
static ResultantDTO from(List<Transaction> transactions) {...}
You could write:
Map<String, List<Transaction>> transactionsGroupedByAccountType = transactions
.stream()
.collect(groupingBy(Transaction::getAccountType));
Map<String, ResultantDTO> result = transactionsGroupedByAccountType.entrySet().stream()
.collect(toMap(Entry::getKey, e -> from(e.getValue)));
You may be able to do it in one stream but this is probably cleaner and simpler.
Upvotes: 2