Using Java streams, I would like to calculate the cost given a list of items, grouped by type. After getting the cost, I would like to map the BigDecimal to a formatted currency String. Is it possible to map reduced and grouped values?
import java.math.BigDecimal; import java.util.List; import java.util.ArrayList; import java.util.stream.Collectors; import java.util.Map; public class MyClass { public static class Item { private String name; private String type; private BigDecimal cost; public Item(String name, String type, BigDecimal cost) { this.name = name; this.type = type; this.cost = cost; } public String getName() { return name; } public String getType() { return type; } public BigDecimal getCost() { return cost; } } public static void main(String args[]) { List<Item> items = new ArrayList<>(); items.add(new Item("Doll", "Toy", new BigDecimal("2.00"))); items.add(new Item("Hamburger", "Food", new BigDecimal("8.00"))); items.add(new Item("Car", "Toy", new BigDecimal("4.00"))); items.add(new Item("Salad", "Food", new BigDecimal("1.00"))); Map<String, BigDecimal> itemTypesAndCosts = items.stream().collect(Collectors.groupingBy(Item::getType, Collectors.reducing(BigDecimal.ZERO, Item::getCost, BigDecimal::add))); // prints {Food=9.00, Toy=6.00} // I would like it to map big decimal to formatted string to print {Food="$9.00", Toy="$6.00"} System.out.println(itemTypesAndCosts); } }
Advertisement
Answer
You can wrap your reducing collector in CollectingAndThen
collector which takes a downstream collector and a finisher function. CollectingAndThen is a special collector that allows us to perform another action on a result straight after collecting ends. Change your map type to Map<String, String>
and do :
Map<String, String> itemTypesAndCosts = items.stream().collect(Collectors.groupingBy(Item::getType, Collectors.collectingAndThen( Collectors.reducing(BigDecimal.ZERO, Item::getCost, BigDecimal::add), total -> new DecimalFormat("'$'0.00").format(total)) )); //output: {Food=$9.00, Toy=$6.00}