Java 8 stream - merge collections of objects sharing the same Id

后端 未结 6 1667
后悔当初
后悔当初 2021-02-13 15:11

I have a collection of invoices :

class Invoice {
  int month;
  BigDecimal amount
}

I\'d like to merge these invoices, so I get one invoice pe

相关标签:
6条回答
  • 2021-02-13 15:51

    I think if your application do not support lambda than this might be a suitable answer eg (Android minSdkVersion=16 do not support lambda)

    public static List<Invoice> mergeAmount(List<Invoice> invoiceList) {
     List<Invoice> newInvoiceList = new ArrayList<>();
      for(Invoice inv: invoiceList) {
        boolean isThere = false;
         for (Invoice inv1: newInvoiceList) {
          if (inv1.getAmount() == inv.getAmount()) {
             inv1.setAmount(inv1.getAmoount()+inv.getAmount());
             isThere = true;
             break;
           }             
         }
        if (!isThere) {
            newInvoiceList.add(inv);
        } 
     }
      return newInvoiceList;
    }
    
    0 讨论(0)
  • 2021-02-13 16:05

    You can do something like

        Map<Integer, Invoice> invoiceMap = invoices.stream()
                .collect(Collectors.groupingBy(                   // group invoices by month
                        invoice -> invoice.month
                ))
                .entrySet().stream()                              // once you have them grouped stream then again so...
                .collect(Collectors.toMap(
                        entry -> entry.getKey(),                  // we can mantain the key (month)
                        entry -> entry.getValue().stream()        // and streaming all month's invoices
                            .reduce((invoice, invoice2) ->        // add all the ammounts
                                    new Invoice(invoice.month, invoice.amount.add(invoice2.amount)))
                                .orElse(new Invoice(entry.getKey(), new BigDecimal(0)))          // In case we don't have any invoice (unlikeable)
                ));
    
    0 讨论(0)
  • 2021-02-13 16:10

    If you could add the following copy constructor and merge method to your Invoice class:

    public Invoice(Invoice another) {
        this.month = another.month;
        this.amount = another.amount;
    }
    
    public Invoice merge(Invoice another) {
        amount = amount.add(another.amount); // BigDecimal is immutable
        return this;
    }
    

    You could reduce as you want, as follows:

    Collection<Invoice> result = list.stream()
        .collect(Collectors.toMap(
            Invoice::getMonth, // use month as key
            Invoice::new,      // use copy constructor => don't mutate original invoices
            Invoice::merge))   // merge invoices with same month
        .values();
    

    I'm using Collectors.toMap to do the job, which has three arguments: a function that maps elements of the stream to keys, a function that maps elements of the stream to values and a merge function that is used to combine values when there are collisions on the keys.

    0 讨论(0)
  • 2021-02-13 16:11

    If you are OK returning a Collection it would look like this:

    Collection<Invoice>  invoices = list.collect(Collectors.toMap(Invoice::getMonth, Function.identity(), (left, right) -> {
                    left.setAmount(left.getAmount().add(right.getAmount()));
                    return left;
                })).values();
    

    If you really need a List:

     list.stream().collect(Collectors.collectingAndThen(Collectors.toMap(Invoice::getMonth, Function.identity(), (left, right) -> {
                    left.setAmount(left.getAmount().add(right.getAmount()));
                    return left;
                }), m -> new ArrayList<>(m.values())));
    

    Both obviously assume that Invoice is mutable...

    0 讨论(0)
  • 2021-02-13 16:11
    Collection<Invoice> result = invoices.stream().collect(groupingBy(i -> i.month,
                    collectingAndThen(
                        reducing((Invoice i1, Invoice i2) -> new Invoice(i1.month, i1.amount + i2.amount)),
                            Optional::get))).values();
    
    0 讨论(0)
  • 2021-02-13 16:13

    Here is the solution by My library: AbacusUtil

    Stream.of(invoices)
          .groupBy2(Invoice::getMonth, Invoice::getAmount, BigDecimal::add)  
          .map(e -> new Invoice(e.getKey(), e.getValue())) // Probably we should not modify original invoices. create new instances.
          .toList();
    
    0 讨论(0)
提交回复
热议问题