One way is to create an object for the set of fields that you are grouping. This class can be created to provide good helper methods.
So, with your original class, as follows:
public final class TaxLine { private String title; private BigDecimal rate; private BigDecimal price; public TaxLine(String title, BigDecimal rate, BigDecimal price) { this.title = title; this.rate = rate; this.price = price; } public String getTitle() { return this.title; } public BigDecimal getRate() { return this.rate; } public BigDecimal getPrice() { return this.price; } @Override public String toString() { return "TaxLine = title:\"" + this.title + "\", rate:" + this.rate + ", price:" + this.price; } }
And an auxiliary grouping class defined as follows:
public final class TaxGroup { private String title; private BigDecimal rate; public static TaxLine asLine(Entry<TaxGroup, BigDecimal> e) { return new TaxLine(e.getKey().getTitle(), e.getKey().getRate(), e.getValue()); } public TaxGroup(TaxLine taxLine) { this.title = taxLine.getTitle(); this.rate = taxLine.getRate(); } public String getTitle() { return this.title; } public BigDecimal getRate() { return this.rate; } @Override public int hashCode() { return this.title.hashCode() * 31 + this.rate.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) return false; TaxGroup that = (TaxGroup) obj; return (this.title.equals(that.title) && this.rate.equals(that.rate)); } }
Your code for combining positions is dividing into several lines to see its various parts:
List<TaxLine> lines = Arrays.asList( new TaxLine("New York Tax", new BigDecimal("0.20"), new BigDecimal("20.00")), new TaxLine("New York Tax", new BigDecimal("0.20"), new BigDecimal("20.00")), new TaxLine("County Tax" , new BigDecimal("0.10"), new BigDecimal("10.00")) ); List<TaxLine> combined = lines .stream() .collect(Collectors.groupingBy(TaxGroup::new, Collectors.reducing(BigDecimal.ZERO, TaxLine::getPrice, BigDecimal::add))) .entrySet() .stream() .map(TaxGroup::asLine) .collect(Collectors.toList());
Then you can print the input / output:
System.out.println("Input:"); lines.stream().forEach(System.out::println); System.out.println("Combined:"); combined.stream().forEach(System.out::println);
To do this:
Input: TaxLine = title:"New York Tax", rate:0.20, price:20.00 TaxLine = title:"New York Tax", rate:0.20, price:20.00 TaxLine = title:"County Tax", rate:0.10, price:10.00 Combined: TaxLine = title:"New York Tax", rate:0.20, price:40.00 TaxLine = title:"County Tax", rate:0.10, price:10.00