Group by two fields, then summing BigDecimal

I have a list of taxes:

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 

TaxLine Class

 public class TaxLine { private BigDecimal price; private BigDecimal rate; private String title; } 

I would like to combine them into a unique title and rate , and then add price , expected:

  TaxLine = title:"New York Tax", rate:0.20, price:40.00 TaxLine = title:"County Tax", rate:0.10, price:10.00 

How to do it in Java 8?

Group by several field names in java 8 , do not summarize a field, it can be grouped by only two fields.

+5
source share
3 answers

The principle is the same as in the related question, you just need another downstream collector to summarize:

 List<TaxLine> flattened = taxes.stream() .collect(Collectors.groupingBy( TaxLine::getTitle, Collectors.groupingBy( TaxLine::getRate, Collectors.reducing( BigDecimal.ZERO, TaxLine::getPrice, BigDecimal::add)))) .entrySet() .stream() .flatMap(e1 -> e1.getValue() .entrySet() .stream() .map(e2 -> new TaxLine(e2.getValue(), e2.getKey(), e1.getKey()))) .collect(Collectors.toList()); 
+8
source

Unable to summarize a grouped field can be achieved by defining a new TitleRate class inside the Taxline class, as shown below.

  class Taxline{ public static class TitleRate { public TitleRate(String title, int taxline) { ... } } public TitleRate getTitleRate() { return new TitleRate(title, taxline); } } 

To summarize the price by grouping the heading and tax, the following can be used.

 Map<TitleRate, List<Taxline>> groupedData = people.collect(Collectors.groupingBy(Taxline::getTitleRate)); List<Taxline> groupedTaxLines = new ArrayList<Taxline>(); BigDecimal groupedRate = BigDecimal.ZERO; for (Map<TitleRate, List<Taxline>> entry : groupedData.entrySet()) { for(Taxline taxline : entry.getValue()){ groupedRate = groupedRate.add(taxline.getPrice()); } groupedTaxLines.add(new Taxline(entry.getKey().getTitle, entry.getKey().getRate(), groupedRate)); groupedRate = BigDecimal.ZERO; } 
0
source

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 
0
source

Source: https://habr.com/ru/post/1262720/


All Articles