英文:
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,
- 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]. - 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,
- 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]. - 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应该是区间的最后一天。
通过year
和month
,我们可以获取区间的正确最后一天。
英文:
Using groupingBy
would help in such a situation. Here is one of the approaches:
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)
)
)
);
// iterate the map to get the output
dateRangeToSumMap.forEach((k, v) -> {
System.out.println("Date Range = " + k + " , Sum = " + 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<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)));
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<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());
Another approach :
You can simplified the code without creating the Partner object the way @Naman suggested
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());
答案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<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;
}
}
Output:
[BiWeeklyReport{amount=800, invoiceDate=2020-01-11}, BiWeeklyReport{amount=800, invoiceDate=2020-01-21}]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论