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?
JavaScript
x
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 :
JavaScript
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}