使用Java streams根据日期间隔对列表数据进行分组,并对金额字段求和

使用Java streams根据日期间隔对列表数据进行分组,并对金额字段求和,java,java-stream,Java,Java Stream,假设有一个类:合作伙伴 Class Partner { LocalDate invoiceDate; BigDecimal amount; } 现在,我有一个按降序排列的伙伴对象列表,例如: [Partner(invoiceDate=2020-01-21, amount=400), Partner(invoiceDate=2020-01-20, amount=400), Partner(invoiceDate=2020-01-11, amount=400), Partn

假设有一个类:合作伙伴

Class Partner {
 LocalDate invoiceDate;  
 BigDecimal amount;  
}
现在,我有一个按降序排列的伙伴对象列表,例如:

[Partner(invoiceDate=2020-01-21, amount=400), 
 Partner(invoiceDate=2020-01-20, amount=400), 
 Partner(invoiceDate=2020-01-11, amount=400), 
 Partner(invoiceDate=2020-01-10, amount=400),
 .....,
 .....]
在上述示例数据中,“invoiceDate”字段是一月的数据。
注意:该列表将包含12个月或以上的数据

现在,

  • 我想每隔15天对数据进行分组。即
    第一个间隔,月的第一天到第十五天[1-15]。
    第二个间隔,从第16天到本月最后一天[16-30/31/28/29]
  • 最后求和日期范围之间的金额值
  • 从上述样本数据计算:
    第一个间隔[1-15]:2行限定=>[invoiceDate=2020-01-11和invoiceDate=2020-01-10]
    第二个间隔[16-31]:2行限定=>[invoiceDate=2020-01-21和invoiceDate=2020-01-20]

    最终输出数据应如下所示:

    [Partner(invoiceDate=2020-01-31, amount=800), 
    Partner(invoiceDate=2020-01-15, amount=800)]
    
    [BiWeeklyReport{amount=800, invoiceDate=2020-01-11}, BiWeeklyReport{amount=800, invoiceDate=2020-01-21}]
    

    注意:在最终输出中,invoiceDate应该是间隔的最后一天。

    我认为这至少可以让您从以下内容开始:

    Function<Partner, LocalDate> getInterval = (partner) -> {
      // compute if day is before 15th of month and return either 15th
      // of month or last day of month
      return null; 
    };
    
    Collection<Partner> partners = Arrays.asList();
    Map<LocalDate, BigDecimal> result = partners.stream()
        .collect(Collectors.toMap(getInterval, Partner::getAmount, (a1, a2) -> a1.add(a2)));
    
    函数getInterval=(合作伙伴)->{
    //计算日期是否在月15日之前,并返回其中一个月15日
    //月份或月份的最后一天
    返回null;
    };
    集合伙伴=Arrays.asList();
    映射结果=partners.stream()
    .collect(Collectors.toMap(getInterval,Partner::getAmount,(a1,a2)->a1.add(a2));
    

    您可以使用带有merge函数的map collector来获取具有相同键的所有元素的总和。

    我认为这至少可以让您从以下内容开始:

    Function<Partner, LocalDate> getInterval = (partner) -> {
      // compute if day is before 15th of month and return either 15th
      // of month or last day of month
      return null; 
    };
    
    Collection<Partner> partners = Arrays.asList();
    Map<LocalDate, BigDecimal> result = partners.stream()
        .collect(Collectors.toMap(getInterval, Partner::getAmount, (a1, a2) -> a1.add(a2)));
    
    函数getInterval=(合作伙伴)->{
    //计算日期是否在月15日之前,并返回其中一个月15日
    //月份或月份的最后一天
    返回null;
    };
    集合伙伴=Arrays.asList();
    映射结果=partners.stream()
    .collect(Collectors.toMap(getInterval,Partner::getAmount,(a1,a2)->a1.add(a2));
    

    您可以使用带有合并功能的地图采集器来获取具有相同键的所有元素的总和。

    在这种情况下,使用
    groupingBy
    会有所帮助。以下是其中一种方法:

    导入静态java.util.stream.collector.*;
    映射dateRangeToSumMap=列表
    .stream()
    .收集(
    分组方式(e->e.invoiceDate.getDayOfMonth()>15?-16-31):“1-16”,
    映射(
    合作伙伴::getAmount,
    减少(BigDecimal.ZERO,BigDecimal::add)
    )
    )
    );
    //迭代映射以获得输出
    dateRangeToSumMap.forEach((k,v)->{
    System.out.println(“日期范围=“+k+”,总和=“+v”);
    });
    
    输出

    日期范围=1-16,总和=800
    日期范围=16-31,总和=800
    
    最终输出数据应如下所示:

    [合作伙伴(发票日期=2020-01-31,金额=800),
    合作伙伴(发票日期=2020-01-15,金额=800)]
    
    这可以用我们现有的地图来构建

    注意:在最终输出中,invoiceDate应为间隔的最后一天


    使用
    ,我们可以得到正确的间隔最后一天。

    使用
    分组方式
    在这种情况下会有所帮助。以下是其中一种方法:

    导入静态java.util.stream.collector.*;
    映射dateRangeToSumMap=列表
    .stream()
    .收集(
    分组方式(e->e.invoiceDate.getDayOfMonth()>15?-16-31):“1-16”,
    映射(
    合作伙伴::getAmount,
    减少(BigDecimal.ZERO,BigDecimal::add)
    )
    )
    );
    //迭代映射以获得输出
    dateRangeToSumMap.forEach((k,v)->{
    System.out.println(“日期范围=“+k+”,总和=“+v”);
    });
    
    输出

    日期范围=1-16,总和=800
    日期范围=16-31,总和=800
    
    最终输出数据应如下所示:

    [合作伙伴(发票日期=2020-01-31,金额=800),
    合作伙伴(发票日期=2020-01-15,金额=800)]
    
    这可以用我们现有的地图来构建

    注意:在最终输出中,invoiceDate应为间隔的最后一天


    使用
    ,我们可以得到正确的间隔最后一天。

    您可以首先将
    发票日期
    映射到15天间隔日期。然后使用
    Collectors.toMap
    将数据映射到for
    invoiceDate
    中,并通过创建新的
    Parter
    对象对合并函数中的
    金额进行求和。最后以列表的形式获取映射值

    Map<LocalDate, Partner> result = list.stream()
        .map(p -> new Partner(p.getInvoiceDate().getDayOfMonth() > 15
            ? p.getInvoiceDate().withDayOfMonth(p.getInvoiceDate().lengthOfMonth())
            : p.getInvoiceDate().withDayOfMonth(15),
              p.getAmount()))
        .collect(Collectors.toMap(Partner::getInvoiceDate, e -> e,
            (a, b) -> new Partner(a.getInvoiceDate(), a.getAmount().add(b.getAmount()))));
    
    List<Partner> res = new ArrayList<>(result.values());
    
    Map result=list.stream()
    .map(p->new Partner(p.getInvoiceDate().getDayOfMonth()>15
    ?p.getInvoiceDate().withDayOfMonth(p.getInvoiceDate().lengthOfMonth())
    :p.getInvoiceDate()。withDayOfMonth(15),
    p、 getAmount())
    .collect(Collectors.toMap)(合作伙伴::getInvoiceDate,e->e,
    (a,b)->新合伙人(a.getInvoiceDate(),a.getAmount().add(b.getAmount());
    List res=新的数组列表(result.values());
    
    另一种方法:

    [Partner(invoiceDate=2020-01-31, amount=800), 
    Partner(invoiceDate=2020-01-15, amount=800)]
    
    [BiWeeklyReport{amount=800, invoiceDate=2020-01-11}, BiWeeklyReport{amount=800, invoiceDate=2020-01-21}]
    
    您可以简化代码,而无需按照@Naman建议的方式创建合作伙伴对象

    static LocalDate getIntervalDate(LocalDate d) {
        return (d.getDayOfMonth() > 15 ? d.withDayOfMonth(d.lengthOfMonth()) 
                                       : d.withDayOfMonth(15));
    }
    
    List<Partner> res = list.stream()
        .collect(Collectors.toMap(e -> getIntervalDate(e.getInvoiceDate()), 
                                  e -> e.getAmount(), (a, b) -> a.add(b)))
        .entrySet()
        .stream()
        .map(e -> new Partner(e.getKey(), e.getValue()))
        .collect(Collectors.toList());
    
    静态LocalDate getIntervalDate(LocalDate d){ 返回(d.getDayOfMonth()>15?d.withDayOfMonth(d.lengthOfMonth()) :d.月(15)日内; } List res=List.stream() .收款(公司)