iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0
自我挑戰組

解三十天的 CodeWars系列 第 14

Human readable duration format

  • 分享至 

  • xImage
  •  

CodeWars 題目

Link

難度

4 kyu

題目

跟之前解過的一題類似,也是把秒數轉換為人類看懂的時間。
格式是:年/日/時/分/秒,並且用英文輸出。

例如:12 days, 5 hours, 38 minutes and 23 seconds。

思路

有兩件大任務,時間 & 字串顯示方式。

分析時間可參考過去看到的最佳解:
把時間用大一階的方式取餘,剩下的就是該時間的數值。

例如:seconds / 60 % 60,先以六十取得以分鐘為單位,再以每小時 60 分,
取得的餘數,也就是分鐘單位的數值,因為多餘的時間將會在取整時被省略。

將時間以 key 單位,用物件拆分,再轉成陣列方便調用。
用 object.entries 轉換成陣列,並驗證陣列長度;只保留 value > 0 的元素,並且如果 value 大於 1,keys + s。

pseudo code

Object.entries(times).filter(item =>{
	if(item[1]>1) item[0] = item[0]+"s";
	return item[1] !== 0
})

switch:
=1
直接返回
=2
用 and 拼接後返回
=3
第一元素key+,,最後尾元素前安插and
=3up
除了倒數兩個元素外每個都key +,,最後尾元素前安插and

實作

function formatDuration(seconds) {
   if (seconds === 0) return "now";
   let times = {}

   if (seconds > 80000) {
      times["year"] = parseInt(seconds / 60 / 60 / 24 / 365);
   }
   times["day"] = parseInt(seconds / 60 / 60 / 24 % 365);
   times["hour"] = parseInt(seconds / 60 / 60 % 24);
   times["minute"] = parseInt(seconds / 60 % 60);
   times["second"] = parseInt(seconds % 60);

   let workArr = Object.entries(times).filter(item => {
      if (item[1] > 1) item[0] = item[0] + "s";
      item = item.reverse();
      return item[0] !== 0
   })

   switch (workArr.length) {
   case 1:
      return workArr.flat().join(" ");
      break;
   case 2:
      workArr.splice(1, 0, "and");
      return workArr.flat().join(" ");
      break;
   case 3:
      workArr[0][1] = workArr[0][1] + ",";
      workArr.splice(workArr.length - 1, 0, "and");
      return workArr.flat().join(" ");
      break;
   default:
      workArr = workArr.map((item, index, array) => {
         if (index < array.length - 2) item[1] = item[1] + ",";
      })
      workArr.splice(workArr.length - 1, 0, "and");
      return workArr.flat().join(" ");
      break;
   }
}

如果秒數過小年份的計算可能會有誤差;因此加一個秒數判斷,在秒數大於一定程度時才計算以年為單位的時間。

其次分別計算出各個單位的時間後,以 Object.entries 形式將物件轉換為:[[”單位”: 單位的值],…] 格式;使用陣列方法 filter 過濾掉值為 0 的時間單位,並且如果值大於 1,則單位就添加 s 的複數字尾。

switch 帶入的是陣列長度,也就是最後輸出字串時總共有幾個時間單位。

並且依照規律,將 , 補充在對應的位置、利用 splice 插入 and,最終都是將陣列平面化後轉成字串。

他人的解法

function formatDuration (seconds) {
  var time = { year: 31536000, day: 86400, hour: 3600, minute: 60, second: 1 },
      res = [];

  if (seconds === 0) return 'now';
  
  for (var key in time) {
    if (seconds >= time[key]) {
      var val = Math.floor(seconds/time[key]);
      res.push(val += val > 1 ? ' ' + key + 's' : ' ' + key);
      seconds = seconds % time[key];
    }
  }
 
  return res.length > 1 ? res.join(', ').replace(/,([^,]*)$/,' and'+'$1') : res[0]
}

這個方法預先把每一個時間單位的 1 的秒數都寫成 time 物件。
並用 for…in 來跑物件的迴圈,比對如果秒數大於等於該單位,則取商並用 Math.floor 取整。

最後 push 到 res 陣列中,並利用取得的 val 以及迭代的 key 拼接單位以及判斷是否加 s。
seconds 則取餘之後再進入下一輪。

最後判斷儲存結果的 res 長度是否大於 1?
如果大於 1:物件內容以 , 輸出成字串後,使用取代功能查找,正則表達式會捕獲最後一個 , 之後的字串。

舉例如果是字串 “3 years, 62 days, 9 hours, 46 minutes, 40 seconds”
會捕獲的是 “ 40 seconds”,用意是在拼接最後的 and。

即使單位只有兩個,因為先以 , 拼接整個字串了,多餘的逗號都會被取代掉。
如果三元運算子 res 長度不大於一,表示只有一個單位,直接輸出即可。

心得

貌似這是最簡單的 4 kyu 題目,不過確實是越來越燒腦了。
其實 5 kyu 就有非常多有趣的邏輯題,可以感受出來歐美那邊對於邏輯方面的學科頗有歷史。

今天是中秋,中秋節快樂!


上一篇
Maximum subarray sum
下一篇
Josephus Permutation
系列文
解三十天的 CodeWars30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言