Let’s take for an example I have list of transaction which contains txnId, paymentCode, amount. Output should be each payment code wise how much amount collected.
I tried to group it based on payment code as key, & list of amount for the payment code, but I am not able to reduce it.
import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; class Payment { Integer txnId; String paymentCode; Integer amount; Payment(Integer txnId, String paymentCode, Integer amount) { this.txnId = txnId; this.paymentCode = paymentCode; this.amount = amount; } public String getPaymentCode() { return this.paymentCode; } public Integer getAmount() { return this.amount; } @Override public String toString() { return "Payment [txnId=" + txnId + ", paymentCode=" + paymentCode + ", amount=" + amount + "]"; } } public class ListGroupingSum { public static void main(String[] args) { List<Payment> payments = new ArrayList<>(); payments.add(new Payment(100, "GPAY", 100)); payments.add(new Payment(101, "CC", 200)); payments.add(new Payment(102, "PAYTM", 300)); payments.add(new Payment(103, "GPAY", 400)); payments.add(new Payment(104, "CC", 500)); // @formatter:off Map<String, List<Integer>> paymentListByCodeAndAmount = payments.stream() .collect(Collectors.groupingBy(Payment::getPaymentCode, Collectors.mapping(Payment::getAmount, Collectors.toList()))); System.out.println(paymentListByCodeAndAmount); //System.out.println(paymentListByCodeAndAmount.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); // @formatter:on } }
Which gives the output of
{CC=[200, 500], GPAY=[100, 400], PAYTM=[300]}
But, expectation is
{CC=700, GPAY=500, PAYTM=300}
Advertisement
Answer
Map<String, Integer> paymentListByCodeAndAmount = payments.stream() .collect(groupingBy(Payment::getPaymentCode, summingInt(Payment::getAmount)));
(with exactly your expected output)
Really, summingInt
is a Collector where groupingBy
will put every group, you can “sum ints” or whatever you want.
For example, if the group data is not directly integers, you can map:
Map<String, Integer> paymentListByCodeAndAmount = payments.stream() .collect(groupingBy(Payment::getPaymentCode, mapping(p -> p.amount * p.txnId, summingInt(x -> x))));
readed as “with payments as stream, grouping by payment code, take the product of the amount and the txnId and give me the sum total”.
But, of course, exists millions of combinations, instead simply sum you could summarize (min, max, avg, …) and do something with this:
Map<String, Double> paymentListByCodeAndAmount = payments.stream() .collect(groupingBy(Payment::getPaymentCode, collectingAndThen(summarizingInt(p -> p.amount * p.txnId), IntSummaryStatistics::getAverage)));
readed as “with payments as stream, grouping by payment code, summarize the product of the amount and the txnId and then give me the average”.