昨天我們做好了一個時間的選擇器,今天則是要依據用戶所選擇的時間來整理資料。這個open api沒有給我們可以選擇時間區間的功能,所以我們一樣拿全部的資料,再自己過濾要的區間及補上不足區間的資料。
首先我們要先來定義一下spec,以免漏掉沒有想到的case:
我們把計算的函式同樣寫在computeData.js這個檔案當中,實際的流程大概會是:
export const groupDataByCustom = (dateRange, data) => {
const { startDate, endDate } = dateRange;
const arr = data.contributions;
// 整理原始資料
const dateKeysData = {};
for (let i = 0; i < arr.length; i += 1) {
const momentObj = moment(arr[i].date);
dateKeysData[momentObj.format('YYYY-MM-DD')] = arr[i].count || 0;
}
// 產生每天資料
const tmp = [];
for (let i = moment(startDate); i.isSameOrBefore(endDate); i.add(1, 'days')) {
tmp.push({
value: dateKeysData[i.format('YYYY-MM-DD')] || 0,
oldWeekNum:
i.weekYear() === Number(i.format('YYYY'))
? i.week()
: i.week() + moment(`${i.format('YYYY')}-01-01`).weeksInYear(),
day: i.day(),
date: i.format('YYYY-MM-DD'),
weekYear: i.weekYear()
});
}
// 將每天資料的陣列以365天為一組
const dataGroupBy365Days = chunk(tmp.reverse(), 365);
// 產生正確的weekNum
for (let i = 0; i < dataGroupBy365Days.length; i+=1) {
dataGroupBy365Days[i] = dataGroupBy365Days[i].reverse();
let currentWeekNum;
let newWeekNum = 0;
for (let j = 0; j < dataGroupBy365Days[i].length; j+=1) {
if (dataGroupBy365Days[i][j].oldWeekNum === currentWeekNum) {
dataGroupBy365Days[i][j].weekNum = newWeekNum;
} else {
newWeekNum += 1
currentWeekNum = dataGroupBy365Days[i][j].oldWeekNum;
dataGroupBy365Days[i][j].weekNum = newWeekNum;
}
}
}
// 組成與之前一樣的資料格式
const result = {};
for (let i = 0; i < dataGroupBy365Days.length; i += 1) {
const el = dataGroupBy365Days[i];
result[`${el[0].date} ~ ${el[el.length - 1].date}`] = dataGroupBy365Days[i];
}
return result;
};
這樣就可以整理出能應用到heatmap的資料囉
我們只要改寫,如果checkbox沒有被勾選的話就應用剛剛寫的function:
const renderHeatMap = useCallback(() => {
if (isAll) {
const groupData = groupDataByYear(data);
return Object.keys(groupData)
.sort()
.reverse()
.map((year) => (
<div className="heatmap-wrapper" key={year}>
<HeatMapWidget
title={`${year} ${!!data.years.find((e) => e.year === year) &&
data.years.find((e) => e.year === year).total} contributions`}
chartData={groupData[year]}
isModalOpen={isModalOpen}
colors={colors.map((item) => item.color)}
/>
</div>
));
}
const groupData = groupDataByCustom(pickerDate, data);
return Object.keys(groupData)
.sort()
.reverse()
.map((range) => (
<div className="heatmap-wrapper" key={range}>
<HeatMapWidget
title={`${range}`}
chartData={groupData[range]}
isModalOpen={isModalOpen}
colors={colors.map((item) => item.color)}
/>
</div>
));
}, [data, colors, isModalOpen, isAll, pickerDate]);
就能得到自己要的時間區間資料囉~
資料的格式整理比較抽象一點,不過只要多練習幾次就會比較知道方向。建議大家可以多刷leet code練習,或是試著接不同的open api來整理,一個計算的過程都是在寫的過程中慢慢去修改才越來越好,一開始可能過程很醜沒關係,先試著把自己要的資料格式整理出來比較重要!
這麼一來這個專案的實作就結束囉!附上程式碼跟專案的demo網址
github: https://github.com/yuanchen1103/2020ironman-github-contributions
demo: https://ironman-github.firebaseapp.com/