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

huangapple go评论75阅读模式
英文:

Using Java streams grouping the list data based on the date intervals and sum the amount field

问题

Consider there is a class: Partner

Class Partner {
    LocalDate invoiceDate;
    BigDecimal amount;
}

Now, I have a list of Partner objects sorted in descending order, for example:

[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),
 ...,
 ...]

In the above sample data, the field "invoiceDate" is for the January month.
note: the list will have data for 12 months or above.

Now,

  1. I want to group the data in a 15-day interval, i.e.
    First interval, 1st day to 15th day of the month [1 - 15].
    Second interval, 16th day to the Last day of the month [16 - 30/31/28/29].
  2. And finally, sum the amount value between the date ranges.

Calculation from the above sample data:
first interval [1-15]: 2 rows qualify => [invoiceDate=2020-01-11 and invoiceDate=2020-01-10]
second interval [16-31]: 2 rows qualify => [invoiceDate=2020-01-21 and invoiceDate=2020-01-20]

The final output data should look like this:

[Partner(invoiceDate=2020-01-31, amount=800),
Partner(invoiceDate=2020-01-15, amount=800)]

note: In the final output, invoiceDate should be the last day of the interval.

英文:

Consider there is a class : Partner

Class Partner {
 LocalDate invoiceDate;  
 BigDecimal amount;  
}

Now, I have a list of Partner objects sorted in descending order eg:

[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),
 .....,
 .....]

In the above sample data the field "invoiceDate" is for January month.
note: the list will have data for 12 months or above.

Now,

  1. I want to group the data in 15 days interval .i.e.
    First interval, 1st day to 15th day of the month [1 - 15].
    Second interval, 16th day to Last day of the month [16 - 30/31/28/29].
  2. And finally sum the amount value between the date range.

Calculation form the above sample data :
first interval [1-15] : 2 rows qualifies => [invoiceDate=2020-01-11 and invoiceDate=2020-01-10]
second interval [16-31] : 2 rows qualifies => [invoiceDate=2020-01-21 and invoiceDate=2020-01-20]

The final output data should look like this :

[Partner(invoiceDate=2020-01-31, amount=800), 
Partner(invoiceDate=2020-01-15, amount=800)]

note : In the final output invoiceDate should be the last day of the interval.

答案1

得分: 1

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

import static java.util.stream.Collectors.*;
Map<String, BigDecimal> dateRangeToSumMap = list
					.stream()
					.collect(
							groupingBy(e -> e.invoiceDate.getDayOfMonth() > 15 ? "16-31" : "1-16",
									   mapping(
											   Partner::getAmount, 
											   reducing(BigDecimal.ZERO, BigDecimal::add)
											   )
									   )
							);

// 遍历映射以获得输出
dateRangeToSumMap.forEach((k, v) -> {
				
				System.out.println("Date Range = " + k + " , Sum = " + v);
				
			});

输出:

Date Range = 1-16 , Sum = 800
Date Range = 16-31 , Sum = 800

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

[Partner(invoiceDate=2020-01-31, amount=800), 
Partner(invoiceDate=2020-01-15, amount=800)]

可以使用我们拥有的映射来构建这个结果。

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

通过yearmonth,我们可以获取区间的正确最后一天。

英文:

Using groupingBy would help in such a situation. Here is one of the approaches:

import static java.util.stream.Collectors.*;
Map&lt;String, BigDecimal&gt; dateRangeToSumMap = list
					.stream()
					.collect(
							groupingBy(e -&gt; e.invoiceDate.getDayOfMonth() &gt; 15 ? &quot;16-31&quot; : &quot;1-16&quot;,
									   mapping(
											   Partner::getAmount, 
											   reducing(BigDecimal.ZERO, BigDecimal::add)
											   )
									   )
							);

// iterate the map to get the output
dateRangeToSumMap.forEach((k, v) -&gt; {
				
				System.out.println(&quot;Date Range = &quot; + k + &quot; , Sum = &quot; + v);
				
			});

Output:

Date Range = 1-16 , Sum = 800
Date Range = 16-31 , Sum = 800

> The final output data should look like this :
>

[Partner(invoiceDate=2020-01-31, amount=800), 
Partner(invoiceDate=2020-01-15, amount=800)]

This can be constructed with the map we have.
> note : In the final output invoiceDate should be the last day of the interval.

With year and month, we can get the correct last day of the interval.

答案2

得分: 0

我认为这至少应该给你一个开始的方向:

Function<Partner, LocalDate> getInterval = (partner) -> {
// 计算如果日期在月初15日之前,则返回月初15日,否则返回月底最后一天
return null;
};

Collection<Partner> partners = Arrays.asList();
Map<LocalDate, BigDecimal> result = partners.stream()
.collect(Collectors.toMap(getInterval, Partner::getAmount, (a1, a2) -> a1.add(a2)));


你可以使用map收集器与合并函数来获取具有相同键的所有元素的总和。
英文:

I think this should give you at least something to start with:

Function&lt;Partner, LocalDate&gt; getInterval = (partner) -&gt; {
  // compute if day is before 15th of month and return either 15th
  // of month or last day of month
  return null; 
};

Collection&lt;Partner&gt; partners = Arrays.asList();
Map&lt;LocalDate, BigDecimal&gt; result = partners.stream()
    .collect(Collectors.toMap(getInterval, Partner::getAmount, (a1, a2) -&gt; a1.add(a2)));

You can use the map collector with the merge function to get the sum for all elements with the same key.

答案3

得分: 0

以下是您要求的代码部分的中文翻译:

您可以首先将invoiceDate映射为15天间隔的日期。然后使用Collectors.toMap将数据映射到invoiceDate和通过创建新的Partner对象在合并函数中求和amount。最后将映射的值获取为列表。

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());

另一种方法:

您可以简化代码,不必像@Naman建议的那样创建Partner对象。

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());
英文:

You can first map invoiceDate into 15-day interval date. Then use Collectors.toMap to map the data into for invoiceDate and sum the amount in merge function by creating new Parter object. Finally get the map values as list.

Map&lt;LocalDate, Partner&gt; result = list.stream()
    .map(p -&gt; new Partner(p.getInvoiceDate().getDayOfMonth() &gt; 15
        ? p.getInvoiceDate().withDayOfMonth(p.getInvoiceDate().lengthOfMonth())
        : p.getInvoiceDate().withDayOfMonth(15),
          p.getAmount()))
    .collect(Collectors.toMap(Partner::getInvoiceDate, e -&gt; e,
        (a, b) -&gt; new Partner(a.getInvoiceDate(), a.getAmount().add(b.getAmount()))));

List&lt;Partner&gt; res = new ArrayList&lt;&gt;(result.values());

Another approach :

You can simplified the code without creating the Partner object the way @Naman suggested

static LocalDate getIntervalDate(LocalDate d) {
    return (d.getDayOfMonth() &gt; 15 ? d.withDayOfMonth(d.lengthOfMonth()) 
                                   : d.withDayOfMonth(15));
}

List&lt;Partner&gt; res = list.stream()
    .collect(Collectors.toMap(e -&gt; getIntervalDate(e.getInvoiceDate()), 
                              e -&gt; e.getAmount(), (a, b) -&gt; a.add(b)))
    .entrySet()
    .stream()
    .map(e -&gt; new Partner(e.getKey(), e.getValue()))
    .collect(Collectors.toList());

答案4

得分: 0

Java的冗长和缺乏元组类型使得代码稍微变得更长但可以在一个步骤内完成无需任何分组

public class Main {
    public static void main(String[] args) {
        List<Partner> partners = Arrays.asList(
            new Partner(LocalDate.of(2020, 1, 21), BigDecimal.valueOf(400)),
            new Partner(LocalDate.of(2020, 1, 20), BigDecimal.valueOf(400)),
            new Partner(LocalDate.of(2020, 1, 11), BigDecimal.valueOf(400)),
            new Partner(LocalDate.of(2020, 1, 10), BigDecimal.valueOf(400))
        );

        Accumulator results = IntStream
            .range(0, partners.size())
            .mapToObj(i -> new AbstractMap.SimpleImmutableEntry<>(i, partners.get(i)))
            .reduce(new Accumulator(), (Accumulator acc, Map.Entry<Integer, Partner> e) -> {
                Partner p = e.getValue();
                if (p.invoiceDate.getMonthValue() == acc.month || e.getKey() == 0) {
                    BiWeeklyReport bucket = p.invoiceDate.getDayOfMonth() <= 15 ? acc.first : acc.second;
                    bucket.amount = bucket.amount.add(p.amount);
                    bucket.invoiceDate = bucket.invoiceDate.isAfter(p.invoiceDate) ? bucket.invoiceDate : p.invoiceDate;
                    acc.month = bucket.invoiceDate.getMonthValue();
                }
                if (e.getKey() == partners.size() - 1 || p.invoiceDate.getMonthValue() != acc.month) {
                    if (acc.first.amount.compareTo(BigDecimal.ZERO) > 0) {
                        acc.reports.add(acc.first);
                    }
                    if (acc.second.amount.compareTo(BigDecimal.ZERO) > 0) {
                        acc.reports.add(acc.second);
                    }
                    acc.first = new BiWeeklyReport();
                    acc.second = new BiWeeklyReport();
                }
                return acc;
            }, (acc1, acc2) -> {
                acc1.reports.addAll(acc2.reports);
                return acc1;
            });
        System.out.println(results.reports);
    }

    private static class Partner {
        LocalDate invoiceDate;
        BigDecimal amount;

        private Partner(LocalDate invoiceDate, BigDecimal amount) {
            this.invoiceDate = invoiceDate;
            this.amount = amount;
        }
    }

    private static class BiWeeklyReport {
        BigDecimal amount = BigDecimal.ZERO;
        LocalDate invoiceDate = LocalDate.of(1970, 1, 1);

        @Override
        public String toString() {
            return "BiWeeklyReport{" +
                "amount=" + amount.longValue() +
                ", invoiceDate=" + DateTimeFormatter.ISO_LOCAL_DATE.format(invoiceDate) +
                '}';
        }
    }

    private static class Accumulator {
        List<BiWeeklyReport> reports = new ArrayList<>();
        BiWeeklyReport first = new BiWeeklyReport();
        BiWeeklyReport second = new BiWeeklyReport();
        int month = -1;
    }
}

**输出:**

[BiWeeklyReport{amount=800, invoiceDate=2020-01-11}, BiWeeklyReport{amount=800, invoiceDate=2020-01-21}]
英文:

Java's verbosity and lack of a tuple type makes the code somewhat longer, this can be done in one pass without any grouping.

public class Main {
public static void main(String[] args) {
List&lt;Partner&gt; partners = Arrays.asList(
new Partner(LocalDate.of(2020, 1, 21), BigDecimal.valueOf(400)),
new Partner(LocalDate.of(2020, 1, 20), BigDecimal.valueOf(400)),
new Partner(LocalDate.of(2020, 1, 11), BigDecimal.valueOf(400)),
new Partner(LocalDate.of(2020, 1, 10), BigDecimal.valueOf(400))
);
Accumulator results = IntStream
.range(0, partners.size())
.mapToObj(i -&gt; new AbstractMap.SimpleImmutableEntry&lt;&gt;(i, partners.get(i)))
.reduce(new Accumulator(), (Accumulator acc, Map.Entry&lt;Integer, Partner&gt; e) -&gt; {
Partner p = e.getValue();
if (p.invoiceDate.getMonthValue() == acc.month || e.getKey() == 0) {
BiWeeklyReport bucket = p.invoiceDate.getDayOfMonth() &lt;= 15 ? acc.first : acc.second;
bucket.amount = bucket.amount.add(p.amount);
bucket.invoiceDate = bucket.invoiceDate.isAfter(p.invoiceDate) ? bucket.invoiceDate : p.invoiceDate;
acc.month = bucket.invoiceDate.getMonthValue();
}
if (e.getKey() == partners.size() - 1 || p.invoiceDate.getMonthValue() != acc.month) {
if (acc.first.amount.compareTo(BigDecimal.ZERO) &gt; 0) {
acc.reports.add(acc.first);
}
if (acc.second.amount.compareTo(BigDecimal.ZERO) &gt; 0) {
acc.reports.add(acc.second);
}
acc.first = new BiWeeklyReport();
acc.second = new BiWeeklyReport();
}
return acc;
}, (acc1, acc2) -&gt; {
acc1.reports.addAll(acc2.reports);
return acc1;
});
System.out.println(results.reports);
}
private static class Partner {
LocalDate invoiceDate;
BigDecimal amount;
private Partner(LocalDate invoiceDate, BigDecimal amount) {
this.invoiceDate = invoiceDate;
this.amount = amount;
}
}
private static class BiWeeklyReport {
BigDecimal amount = BigDecimal.ZERO;
LocalDate invoiceDate = LocalDate.of(1970, 1, 1);
@Override
public String toString() {
return &quot;BiWeeklyReport{&quot; +
&quot;amount=&quot; + amount.longValue() +
&quot;, invoiceDate=&quot; + DateTimeFormatter.ISO_LOCAL_DATE.format(invoiceDate) +
&#39;}&#39;;
}
}
private static class Accumulator {
List&lt;BiWeeklyReport&gt; reports = new ArrayList&lt;&gt;();
BiWeeklyReport first = new BiWeeklyReport();
BiWeeklyReport second = new BiWeeklyReport();
int month = -1;
}
}

Output:

[BiWeeklyReport{amount=800, invoiceDate=2020-01-11}, BiWeeklyReport{amount=800, invoiceDate=2020-01-21}]

huangapple
  • 本文由 发表于 2020年8月5日 17:21:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/63262101.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定