需求
項目中經常會遇到折線圖等隨日期、月份變化的趨勢圖,但有時會出現日期不連續的情況,圖表就會有中斷,需要在后台將日期數據補全,sql或者程序里都可以處理。
而Java8提供的無限流,可以更方便的實現補全日期的操作,因為日期是連續遞增的,看似無限序列。
原始數據
自2020-11-27 過去一周的數據,日期不連續
demo
補全日期數據,沒有的日期,數據默認補0
Controller
/**
* 折線圖數據
*
* @param preDate 開始日期,不傳默認近一周
* @return
*/
@GetMapping("chart")
public List<DailyDataChartVo> getChartData(@RequestParam(value = "date", required = false)
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate preDate) {
return this.dailyDataService.getChartData(preDate);
}
Service
/**
* 折線圖數據
*
* @param preDate 開始日期,不傳默認近一周
* @return
*/
public List<DailyDataChartVo> getChartData(LocalDate preDate) {
if (Objects.isNull(preDate)) {
preDate = LocalDate.now().minusWeeks(1);
}
LocalDate endDate = LocalDate.now();
List<DailyDataChartVo> list = this.dailyDataMapper.selectChartData(preDate, endDate);
return this.completeData(preDate, endDate, list);
}
/**
* 補全數據
*
* @param preDate 開始日期
* @param endDate 截止日期
* @param oldList 未補全的列表
* @return 補全后的列表
*/
private List<DailyDataChartVo> completeData(LocalDate preDate, LocalDate endDate, List<DailyDataChartVo> oldList) {
List<DailyDataChartVo> newList = new ArrayList<>();
if (CollectionUtils.isEmpty(oldList)) {
return newList;
}
//間隔的日期列表
List<LocalDate> dates = this.getRangeDays(preDate, endDate);
Map<LocalDate, DailyDataChartVo> map = oldList.stream()
.collect(Collectors.toMap(DailyDataChartVo::getDate, Function.identity()));
dates.forEach(c -> {
if (map.containsKey(c)) {
newList.add(map.get(c));
} else {
//沒有這一天的數據,默認補0
newList.add(new DailyDataChartVo(c, BigDecimal.ZERO));
}
});
return newList;
}
/**
* 獲取間隔的日期列表
*
* @param preDate 開始日期
* @param endDate 截止日期
* @return
*/
private List<LocalDate> getRangeDays(LocalDate preDate, LocalDate endDate) {
List<LocalDate> dates = new ArrayList<>();
//間隔的天數
long betweenDays = ChronoUnit.DAYS.between(preDate, endDate);
if (betweenDays < 1) {
//開始日期<=截止日期
return dates;
}
//創建一個從開始日期、每次加一天的無限流,限制到截止日期為止
Stream.iterate(preDate, c -> c.plusDays(1))
.limit(betweenDays + 1)
.forEach(dates::add);
return dates;
}
補全后的數據
[
{
"date": "2020-11-20",
"revenue": 22.88
},
{
"date": "2020-11-21",
"revenue": 93.06
},
{
"date": "2020-11-22",
"revenue": 0
},
{
"date": "2020-11-23",
"revenue": 7.99
},
{
"date": "2020-11-24",
"revenue": 0
},
{
"date": "2020-11-25",
"revenue": 50.98
},
{
"date": "2020-11-26",
"revenue": 0
},
{
"date": "2020-11-27",
"revenue": 0
}
]
核心方法
Stream.iterate():接收一個初始元素seed,生成從seed到f的迭代流
/**
* @param seed 初始元素
* @param f UnaryOperator,函數式接口,接收T類型參數,調用apply后返回T本身,應用於上一個元素以產生新元素
*/
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {}
//創建一個從開始日期、每次加一天的無限流,限制到截止日期為止
Stream.iterate(preDate, c -> c.plusDays(1))
.limit(betweenDays + 1)
.forEach(dates::add);
利用無限流,得到連續的日期list,遍歷,有該日的數據直接填充,沒有默認補0。