iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 28
0
Modern Web

資料視覺化!D3入門到實戰系列 第 28

Day28 實戰!Github Heat Map 產生器_整理時間區間資料

  • 分享至 

  • xImage
  •  

昨天我們做好了一個時間的選擇器,今天則是要依據用戶所選擇的時間來整理資料。這個open api沒有給我們可以選擇時間區間的功能,所以我們一樣拿全部的資料,再自己過濾要的區間及補上不足區間的資料。

整理資料格式

首先我們要先來定義一下spec,以免漏掉沒有想到的case:

  1. 勾選All Contributions: 依據api回傳的資料全部顯示,且以年份區分(就是現在的圖表)。
  2. 沒有勾選All Contributions、<= 365天: 一張圖,格子的數量=天數。
  3. 沒有勾選All Contributions、> 365天:以365天為一單位(不考慮閏年)。

我們把計算的函式同樣寫在computeData.js這個檔案當中,實際的流程大概會是:

  1. 將原始資料整理成以date為key的object,到時候在取用資料時會比較容易,也不用一直跑回圈。
  2. 產生範圍內每一天的資料組成一個陣列。
  3. 將每天資料的陣列以365天為一組。
  4. 將每一組資料組成跟上次以年為單位一樣的格式。
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的資料囉

https://ithelp.ithome.com.tw/upload/images/20191003/20119937LrMY25FA7G.png

套用到圖表

我們只要改寫,如果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]);

就能得到自己要的時間區間資料囉~
https://ithelp.ithome.com.tw/upload/images/20191004/20119937dX64xPdiYh.png

補充

資料的格式整理比較抽象一點,不過只要多練習幾次就會比較知道方向。建議大家可以多刷leet code練習,或是試著接不同的open api來整理,一個計算的過程都是在寫的過程中慢慢去修改才越來越好,一開始可能過程很醜沒關係,先試著把自己要的資料格式整理出來比較重要!


這麼一來這個專案的實作就結束囉!附上程式碼跟專案的demo網址

github: https://github.com/yuanchen1103/2020ironman-github-contributions
demo: https://ironman-github.firebaseapp.com/


上一篇
Day27 實戰!Github Heat Map 產生器_製作時間選擇器
下一篇
Day29 推薦進修方向與學習資源
系列文
資料視覺化!D3入門到實戰30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言