# [JS30] Day.18 Tally String Times with Reduce ###### tags: `JS30` ## 任務 Task 將所有影片的時間加總得出總時間。 ==完成時間:10min== ## 步驟 Step 1. 用 `String.split()` 將時間文字轉換成 `array` 分別放入分、秒。 2. 用 `Array.reduce()` 將所有秒數加起來。 3. 轉換成 時、分、秒。 ## 筆記 Note ### <font color=#337EA9>JS String.prototype.split</font> * 語法:`String.split(separator, limit)` * 將字串分割成 `Array` ,`separator` 為指定分割符號, `limit` 為限制最多回傳 `element` 項目(非必要)。 ```javascript= const fruit = 'apple'; console.log(fruit.split("",3)); //output: ['a','p','p'] ``` ### <font color=#337EA9>JS Array.prototype.reduce()</font> * 將一個累加器及每個項目傳入 `callback function`。 * 語法:`Array.reduce(callback(accumulator, currentValue, currentIndex, array), initialValue)` * `accumulator` :累加器,會將回傳項目不斷的加進去。 * `currentValue` :由左至右的 `element`。 * `currentIndex` :`currentValue` 的 `Index`。 * `initialValue` :累加器最一開始的值,如果沒設則為第一個 `element` 項目,且 `currentIndex` 為 `1`。 ## 遇到問題 Problem :::danger ⚠️ <font color=black>Problem</font> ::: * 在將時間轉換成秒時,使用 `split` 將時間轉換成 `[min, second]` ,在加總時會發現 `min` 、 `second` 為 String 所以再加一個 `.map(parseFloat)`。 ```javascript= const seconds = timeNodes .map(node => node.dataset.time) .map(timeCode => { const [mins, secs] = timeCode.split(":").map(parseFloat);//轉成小數; return mins * 60 + secs; }) console.log(seconds); //output: (58)[343, 153, 225, 47, 321, 416...] ``` * 問題來了,那如果將 `parseFloat` 換成 <font color=red>`parseInt`</font>呢? ```javascript=4 const [mins, secs] = timeCode.split(":").map(parseInt); ``` * 得到的結果卻是: ![](https://i.imgur.com/yQJrYgb.jpg) * 怎麼會這樣?!把 `min` 跟`second` 拆開來看: ```javascript=4 const [mins, secs] = timeCode.split(":").map(parseInt); console.log([mins, secs]); ``` ![](https://i.imgur.com/yl7JVlw.jpg) * 只有 `secs` 的地方是 `NaN` ,這就奇怪了?照理說應該要馬就是兩個都是 `NaN` 。 * 看一下 `parseInt` 的 MDN: :::warning *<font color=black>parseInt(string, radix); radix : 幾進位,範圍為 2~36。</font>* ::: * 再看一下 `Array.map()` 的MDN :::warning *<font color=black>let new_array = arr.map(function callback( currentValue[, index[, array]]) { // return element for new_array }[, thisArg])</font>* ::: * 因為在使用 `array.map(parseInt)` 是直接呼叫 `parseInt` 所以在預設情況下,每個元素會轉換成: :::warning *<font color=black>parseInt(currentValue, index, array);</font>* ::: * 舉個例子來看: ```javascript= const a = [1 ,4, 10]; console.log(a.map(parseInt)); //output: [1, NaN, 2] ``` * 將每個項目拆解開來就會是: ```javascript= parseInt(1, 0, [1, 4, 10]);//0進位預設為10進位,得出1 parseInt(4, 1, [1, 4, 10]);//沒有1進位,得出NaN parseInt(10, 2, [1, 4, 10]);//10的2進位為2 ``` * 那 `float` 沒有這個問題嗎?看一下 MDN :::warning *<font color=black>parseFloat(string); </font>* ::: * `float` 每有預設的 `radix` 就不會有進位的問題拉! [參考](https://medium.com/hybrid-maker/%E7%82%BA%E4%BB%80%E9%BA%BC-1-7-11-map-parseint-%E6%9C%83%E5%9B%9E-1-nan-3-6ce698c75de) :::info :ok_hand: <font color=black>Revise</font> ::: * 解決方法: 1. 用 `float` 完美解決。 2. 如果真的想用 `parseInt` ,就把 `callback function` 完整的寫出來就沒問題啦~ ```javascript= const [mins, secs] = timeCode.split(":").map(el => parse(el)); console.log([mins, secs]); ``` ![](https://i.imgur.com/1w8bUUJ.jpg)