# [Codewars] 記錄厲害的解答 ###### tags: `解題筆記` ## 1. Welcome. In this kata, you are asked to square every digit of a number and concatenate them. For example, if we run 9119 through the function, 811181 will come out, because 92 is 81 and 12 is 1. Note: The function accepts an integer and returns an integer ### 厲害的解答 :::spoiler ```javascript= function squareDigits(num){ return Number(('' + num).split('').map(function (val) { return val * val;}).join('')); } ``` ::: 1. 用 '' + number 可以強制把 number 轉為 string 2. str.split(要區隔的條件) 會回傳一個符合區隔條件的陣列 3. arr.join(要結合的條件) 會回傳一個結合完畢的陣列 ### 我的方法 ```javascript= function squareDigits (num) { const myNum = changeTypeToString(num); const arr = createArr(myNum); const result = doCount(arr); const answer = parseInt(combine(result)); console.log(typeof (answer)); return answer; } function changeTypeToString (num) { return num.toString(); } function createArr (num) { return Array.from(num); } function doCount (arr) { return arr.map((value) => value * value); } function combine (result) { return result.join(''); } ``` ## 2. 把文字的第一個字都變成大寫 "How can mirrors be real if our eyes aren't real" should be "How Can Mirrors Be Real If Our Eyes Aren't Real" ### 厲害的解答 :::spoiler ```javascript= String.prototype.toJadenCase = function () { function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } return this.split(' ').map(capitalizeFirstLetter).join(' '); }; ``` ::: 1. 建造一個 callBack 放在 map 裡面 2. str.split(分割的條件) 以 str 為基準回傳達成分割條件的陣列 3. str.charAt(index) 會回傳該 str index 對應的 str 4. str.slice(beginIndex, endIndex) === str.substring(beginIndex, endIndex) => str.slice(beginIndex), str.substring(beginIndex) 會回傳 beginIndex 之後的 str ### 我的方法 ```javascript= function squareDigits (num) { const myNum = changeTypeToString(num); const arr = createArr(myNum); const result = doCount(arr); const answer = parseInt(combine(result)); console.log(typeof (answer)); return answer; } function changeTypeToString (num) { return num.toString(); } function createArr (num) { return Array.from(num); } function doCount (arr) { return arr.map((value) => value * value); } function combine (result) { return result.join(''); } ``` ## 2. 找出最大的收益 Return the most profit from stock quotes. Stock quotes are stored in an array in order of date. The stock profit is the difference in prices in buying and selling stock. Each day, you can either buy one unit of stock, sell any number of stock units you have already bought, or do nothing. Therefore, the most profit is the maximum difference of all pairs in a sequence of stock prices.   > [ 1, 2, 3, 4, 5, 6 ] => 15 (buy at 1,2,3,4,5 and then sell all at 6) [ 6, 5, 4, 3, 2, 1 ] => 0 (nothing to buy for profit) [ 1, 6, 5, 10, 8, 7 ] => 18 (buy at 1,6,5 and sell all at 10) [ 1, 2, 10, 3, 2, 7, 3, 2 ] => 26 (buy at 1,2 and sell them at 10. Then buy at 3,2 and sell them at 7) ![](https://i.imgur.com/15XvE4G.jpg) ### 厲害的解答 ::: spoiler ```javascript= function getMostProfitFromStockQuotes(quotes) { let result = 0; quotes.forEach((stock, index) => { let maxProfit = 0; for (let i = index + 1; i < quotes.length; i++) { const price = quotes[i] - stock; if (price > maxProfit) { maxProfit = price; } } result += maxProfit; }); return result; } ``` ::: ## 3. 字串反射 Description: Deoxyribonucleic acid (DNA) is a chemical found in the nucleus of cells and carries the "instructions" for the development and functioning of living organisms. If you want to know more: http://en.wikipedia.org/wiki/DNA In DNA strings, symbols "A" and "T" are complements of each other, as "C" and "G". You function receives one side of the DNA (string, except for Haskell); you need to return the other complementary side. DNA strand is never empty or there is no DNA at all (again, except for Haskell). More similar exercise are found here: http://rosalind.info/problems/list-view/ >Example: (input --> output) >"ATTGC" --> "TAACG" >"GTAT" --> "CATA" ### 我的方法 一個一個字比對,如果符合就推入對應的結果,但看起來就很繁瑣。 ```javascript= function DNAStrand(dna){ const dnaArray = Array.from(dna); const result = []; dnaArray.forEach((v) => { if (v === 'T') { result.push('A'); } else if ( v === 'A') { result.push('T'); } else if (v === 'C') { result.push('G'); } else if (v === 'G') { result.push('C'); } }); return result.join(''); } ``` ### 厲害的解答 ::: spoiler ```javascript= // 先整理資料 var pairs = {'A':'T','T':'A','C':'G','G':'C'}; function DNAStrand(dna){ return dna.split('').map(function(v){ return pairs[v] }).join(''); } // 1. input 是字串,所以透過方法(split)把字串分隔成陣列 // 2. map 會從陣列的頭跑到陣列的尾,寫入 callback 讓得出的值當作 obj 的 keyName 讀取 value // 3. Array.join('') 把陣列合併成沒有空隙的字串 ``` ::: ## 4. 我有兩個陣列,第二個陣列的值我都不要,請幫我從第一個陣列篩掉我不要的值 Your goal in this kata is to implement a difference function, which subtracts one list from another and returns the result. ```javascript= arrayDiff([1,2],[1]) == [2] ``` If a value is present in b, all of its occurrences must be removed from the other: ```javascript= arrayDiff([1,2,2,2,3],[2]) == [1,3] ``` 有兩個參數(A, B),兩參數都是陣列,函式運算後必須回傳一陣列,該陣列的值必須以參數 A 為基準,但是不可以有參數 B 陣列中的值,另外還需要注意值的順序。 ### 我的方法 b 也是陣列,所以就直接拿 b 跑迴圈,再用 `Array.filter()` 篩掉多的值,但是這樣子會跑很多次迴圈,因為每次外層迴圈結束後又要再用 `Array.filter()` 跑陣列全部的值篩選。 ```javascript= function arrayDiff(a, b) { let result = []; // a 為 [] 就回傳 [] if (a.length === 0) { return result; } // b 為 [] 就回傳 a (原陣列) if (b.length === 0) { result = a; return result; } for (let i = 0; i < b.length; i++) { // 第一次需要用 a 來當作基底陣列 if (i === 0) { result = a.filter((value) => value !== b[i]); // 第二次開始就直接用回傳的 result 再跑陣列篩選 } else { result = result.filter((value) => value !== b[i]); } } return result; } ``` ### 厲害的解答 `Array.filter()` 會完整地跑一次迴圈,代表陣列中的*每一個值都不會漏掉*。所以以參數 A 陣列為基底陣列執行 `Array.filter()`,因為要得出的結果是不可包含 B 陣列的任何一值,所以在 callback 以參數 B 陣列為基底陣列執行 `Array.includes()` + 判斷條件就可以得出需求條件。 ::: spoiler ```javascript= function array_diff(a, b) { return a.filter(e => !b.includes(e)); } ``` ::: ## 5. 請幫我刪除這個陣列中出現過多次的值 Enough is enough! Alice and Bob were on a holiday. Both of them took many pictures of the places they've been, and now they want to show Charlie their entire collection. However, Charlie doesn't like these sessions, since the motive usually repeats. He isn't fond of seeing the Eiffel tower 40 times. He tells them that he will only sit during the session if they show the same motive at most N times. Luckily, Alice and Bob are able to encode the motive as a number. Can you help them to remove numbers such that their list contains each number only up to N times, without changing the order? Task Given a list lst and a number N, create a new list that contains each number of lst at most N times without reordering. For example if N = 2, and the input is [1,2,3,1,2,1,2,3], you take [1,2,3,1,2], drop the next [1,2] since this would lead to 1 and 2 being in the result 3 times, and then take 3, which leads to [1,2,3,1,2,3]. ```javascript= deleteNth ([1,1,1,1],2) // return [1,1] deleteNth ([20,37,20,21],1) // return [20,37,21] ``` 一個函式接兩個參數,第一個參數為陣列,第二個為數字。回傳一個陣列,陣列必須以參數 A 為基準,其值最多只能重複 B(第二個參數)次,多過重複次數的值必須刪除,但是還是要保留原本參數 A 陣列的順序。 ### 我的方法 1. 因為有限定回傳的陣列每個值不能重複 B 次,所以先用 `Array.reduce()` 得出每個值在 A 陣列重複的次數為何 2. 以 A 為基底陣列執行 `Array.forEach()`,寫入條件 > - 有超過重複次數的值才要檢查 > - 還需要檢查當前陣列該值的重複情形 > - 都沒問題才可以推入解答的陣列中 ```javascript= function deleteNth(arr, n) { const sort = arr.reduce((accumulator, currentValue) => { if (accumulator[currentValue]) { accumulator[currentValue]++; } else { accumulator[currentValue] = 1; } return accumulator; }, {}); const array = [...arr]; const result = []; array.forEach((value) => { if (sort[value] > n) { const checker = result.filter((c) => c === value).length; if (checker < n) { result.push(value); } } else { result.push(value); } }); return result; } ``` ### 厲害的解答 透過物件的方式存取資料,再用 `[] bracket notation` 取得物件的 value。 `Array.filter()` 會完整地跑一次迴圈,代表陣列中的*每一個值都不會漏掉*。 以參數 arr 為基底陣列執行 `Array.filter()`,再 callback 寫入任務及條件: > `[] bracket notation` 物件取值 -> 建立及更新資料 > 參數 x 為限定次數的條件,因為在同一圈已經更新資料,所以直接寫入條件即可 ::: spoiler ```javascript= function deleteNth(arr,x) { var cache = {}; return arr.filter(function(n) { // 迴圈的開始先更新資料 // true || false -> 如果第一個為 true 就回傳第一個,反之則回傳第二個 // true && false -> 如果第一個為 true 就回傳第二個,反之則回傳第一個 cache[n] = (cache[n]||0) + 1; return cache[n] <= x; }); } ``` ::: ## 6. 找出 String 中單位詞最小的是多少——Shortest Word Simple, given a string of words, return the length of the shortest word(s). String will never be empty and you do not need to account for different data types. 輸入一個字串的引數叫用函式,函式必須回傳該引數內最短的單詞長度。 ### 我的方法 1. 因為輸入的是字串,且為英文語法,所以用 `String.split(' ')` 將單詞之間的空白間隔移除,並結合成一個陣列 2. `Array.reduce()` 比較每個值的長度 > - 如果 `accumulator` 的長度 > 下一個單詞的長度,就代表下一個單詞的長度比較短,更新 `accumulator` 的值,讓它繼續在下一輪比較 ```javascript= function findShort(s){ return s.split(' ').reduce((accumulator, currentValue) => { if (accumulator.length > currentValue.length) { accumulator = currentValue; } return accumulator; }).length; } ``` 一開始的寫法,但是會報錯。 為什麼? 因為 `accumulator = currentValue.length` 那 `accumulator` 就會變成一個數字了,這樣子就不是以 **單詞的長度與單詞的長度比較,而是數字的長度與單詞的長度比較**,就不是需要的結果了。 ```javascript= function findShort(s){ return s.split(' ').reduce((accumulator, currentValue) => { if (accumulator.length > currentValue.length) { // 下一輪就會變成 // if (4.length > currentValue.length) .... accumulator = currentValue.length; } return accumulator; }); } ``` ### 厲害的解答 化解陣列 + 判斷長度一起來 ==`.apply()` 也可以展開陣列== 1. 用 `String.split(' ')` 將字串中彼此單詞的字串空白刪除,並變成陣列 2. `Array.map()` 會跑整個陣列的緣故,透過 callback 創造出新的陣列 > - 得到原來陣列的每個值的長度:`Array.map((word) => word.length);` 3. 因為 `Math.min()` 不接受陣列作為引數呼叫,所以用 `.apply()` 展開陣列 >- `.apply(null, array)` 可以破除該陣列,效果就像是 ES6 的 ...(展開運算子 spread operator)差不多 >- `Math.min(...[1, 2])` = `Math.min.apply(null, [1, 2])` 4. `Math.min()` 做它該做的工作,找出最小的數字(也就是最小的單詞長度) ::: spoiler ```javascript= function findShort(s){ return Math.min.apply(null, s.split(' ').map(w => w.length)); } ``` ::: 1. [Function.prototype.apply()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#using_apply_to_append_an_array_to_another) 2. [Get the Min/Max elements of an Array in JavaScript](https://bobbyhadz.com/blog/javascript-find-min-max-array) ## 7. 找出陣列中唯一不同的值——Find the unique number There is an array with some numbers. All numbers are equal except for one. Try to find it! ```javascript= findUniq([ 1, 1, 1, 2, 1, 1 ]) === 2 findUniq([ 0, 0, 0.55, 0, 0 ]) === 0.55 ``` It’s guaranteed that array contains at least 3 numbers. The tests contain some very huge arrays, so think about performance. 找出陣列中唯一不同的值並回傳。 ### 我的方法 1. 統整出陣列中值出現的次數 > `Array.reduce()` 配合物件統計值的出現次數 2. `Object.entries()` 將 `Array.reduce()` 得出的物件轉為陣列 > `Object.entries()` -> [[array1], [array2]] 3. `Array.filter()` 篩出只有出現一次的值 > 因為 `Object.enties()` 得出的是陣列包陣列 [[]],所以 `Array.filter()` 得出的也會是陣列包陣列(value),因此需要寫兩次 index 拆掉陣列 ```javascript= function findUniq(arr) { const reduced = arr.reduce((accumulator, currentValue) => { if (!accumulator[currentValue]) { accumulator[currentValue] = 1; } else { accumulator[currentValue] ++; } return accumulator; }, {}); const entries = Object.entries(reduced); return Number(entries.filter((pair) => pair[1] === 1)[0][0]); } ``` ### 厲害的解答 `Array.find()` 會跑全陣列,並且回傳第一個符合 callback 條件的值,便停止繼續跑陣列。 找出「只有出現一次」的值 > `Array.indexOf()` + `Array.lastIndexOf()` 的組合,當兩個方法回傳相同的 index => 該值在此陣列只出現一次。 ::: spoiler ```javascript= function findUniq(arr) { return arr.find(n => arr.indexOf(n) === arr.lastIndexOf(n)); } ``` ::: ## 8. 自己創造 `Math.round`, `Math.ceil` 以及 `Math.floor` —— Math Issues Oh no, our Math object was "accidently" reset. Can you re-implement some of those functions? We can assure, that only non-negative numbers are passed as arguments. So you don't have to consider things like undefined, null, NaN, negative numbers, strings and so on. Here is a list of functions, we need: Math.round() Math.ceil() Math.floor() 寫出個人版本的 `Math.round`, `Math.ceil` 以及 `Math.floor`。 ### 我的方法 #### 1. `Math.round()` 輸入數為小數的話就會看小數點後第一位四捨五入。 > 手動寫邏輯 > 1. 判斷是否有小數點(先轉為陣列後再用 `Array.find()` 檢查有沒有小數點) > 2. 如果有的話再判斷小數點後一位是否 >= 5 > 3.1 如 >= 5,小數點以前的數 + 1 > 3.2 如 < 5,小數點以前的數不動 > 4. 把陣列拼回去變成數字回傳 ```javascript= Math.round = function(number) { let result; const split = String(number).split(''); const isNotInteger = split.find((v) => v === '.') ? true : false; if (isNotInteger) { const pointIndex = split.indexOf('.'); let numbersBeforePoint = ''; for (let i = 0; i < pointIndex; i ++) { numbersBeforePoint += split[i]; if (i + 1 === pointIndex) { numbersBeforePoint = Number(numbersBeforePoint); } } if (Number(split[pointIndex + 1]) >= 5) { result = numbersBeforePoint + 1; } else { result = numbersBeforePoint; } } else { result = Number(split.join('')); } return result; } ``` #### 2. `Math.ceil()` 回傳該數的最大正整數 1. 判斷是否為小數,如果不是就回傳數字本身 > 字串轉陣列,檢查有沒有 '.' > `const isNotInteger = split.find((str) => str === '.') ? true : false;` 2. 如果是小數且 > 0,抓取小數點前的數,如果數字 > 0,小數點前的數字無條件 + 1 回傳,反之就負數,就回傳抓出的數字 ```javascript= Math.ceil = function(number) { let result; const split = String(number).split(''); console.log(split); const isNotInteger = split.find((str) => str === '.') ? true : false; if (isNotInteger) { const pointIndex = split.indexOf('.'); let numbersBeforePoint = ''; // 以小數點的 index 為終點,執行迴圈拼出小數點前的數字 for (let i = 0; i < pointIndex; i ++) { numbersBeforePoint += split[i]; if (i + 1 === pointIndex) { numbersBeforePoint = Number(numbersBeforePoint); } } // 判斷 number 有沒有 > 0 if (number > 0) { // 有的話就執行 Math.ceil 的任務,回傳該數最大的正整數 result = numbersBeforePoint + 1; } else { // 反之就回傳負數本身 result = numbersBeforePoint; } } else { result = number; } return result; } ``` #### 3. `Math.floor` 回傳該數的最小整數 1. 判斷是否為小數,如不是小數就回傳自己 2. 抓去小數點前的數 3. 回傳小數點前的數 ```javascript= Math.floor = function(number) { let result; const split = String(number).split(''); const isNotInteger = split.find((str) => str === '.') ? true : false; if (isNotInteger) { const pointIndex = split.indexOf('.'); let numbersBeforePoint = ''; for (let i = 0; i < pointIndex; i ++) { numbersBeforePoint += split[i]; if (i + 1 === pointIndex) { numbersBeforePoint = Number(numbersBeforePoint); } } if (number > 0) { result = numbersBeforePoint; } else { result = numbersBeforePoint - 1; } } else { result = number; } console.log(result); return result; }; ``` ### [厲害的解答](https://www.youtube.com/watch?v=av2lv0HRthw) ==parseInt(x) 不僅可以把字串 x 轉為數字 x,當 x 遇到自動轉型無法成為數字的話就回停止,因此也可以用來省略小數點後的數(因為遇到小數點便停止)。== #### 1. `Math.round` **判斷數是不是小數,如果是的話就判斷要不要進位,不是的話就直接回傳原數。** 1. 判斷是不是小數 > % 取餘數的方式可以知道是不是小數:`x % 1` 如果是整數的話就不會有餘數,因為整數除以 1 就會得到整數自己。 2. 因為是小數點後第一位的四捨五入,所以餘數 >= 0.5 就要進位,使用 `parseInt()` 遇到非「數字」就停止,所以不僅可以用來字串轉數字,也可以數字省略小數點後的數 > parseInt('1231') -> 1231 > parseInt(1.23) -> 1(因為遇到非數字就停止,所以遇到小數點便停止) ::: spoiler ```javascript= Math.round = function(number) { if (number % 1 >= 0.5) { return parseInt(number) + 1; } else { // 其他整數或小數點後 < 0.5 就用 parseInt() 回傳整數的部分 return parseInt(number); } }; ``` ::: #### 2. `Math.ceil` **是小數且 > 0 就自動進位。** 1. 判斷是否為小數,不是的話就回傳數本身 > x % 1 > 0 2. 且如 > 0 就自動進位(因為 parseInt 可以輸入數字,遇到非數字就停止,所以遇到小數點後就停止) ::: spoiler ```javascript= Math.ceil = function(number) { if (number % 1 > 0 && number > 0) { return parseInt(number) + 1; } return parseInt(number); }; ``` ::: #### 3. `Math.floor` ::: spoiler ```javascript= Math.floor = function(number) { return parseInt(number); }; ``` ::: ## 9. 記錄符合英文字母順序的字串 —— Alphabet symmetry Consider the word "abode". We can see that the letter a is in position 1 and b is in position 2. In the alphabet, a and b are also in positions 1 and 2. Notice also that d and e in abode occupy the positions they would occupy in the alphabet, which are positions 4 and 5. Given an array of words, return an array of the number of letters that occupy their positions in the alphabet for each word. For example, solve(["abode","ABc","xyzD"]) = [4, 3, 1] See test cases for more examples. Input will consist of alphabet characters, both uppercase and lowercase. No spaces. 一個陣列 A 內有數個字串,需要回傳一個陣列 B 其值為陣列 A 每個值(也就是字串)中有多少個字母按照 A-Z 字母排序。 ### 我的方法 >1. 透過 Object Pair 整理字母順序 >2. 陣列,要檢查每一個 + return 陣列 -> `Array.map()` >3. 簡化標準 -> `String.toLowerCase()` >4. 為了方便地檢查,需要再用成陣列,透過陣列方法得到需要的東西 -> `String.split()` + `Array.forEach()` >==`Array.map()` 會從頭到尾跑迴圈,也可以判斷得到自己要的資料== ```javascript= const alphabetObj = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15, p: 16, q: 17, r: 18, s: 19, t: 20, u: 21, v: 22, w: 23, x: 24, y: 25, z: 26 }; function solve (array) { const result = array.map((string) => { const split = string.split(''); // Array.map() 是以陣列的值為單位跑迴圈,所以 checker 到下一個 value 就會初始化(0) let checker = 0; split.forEach((value, index) => { if (alphabetObj[value.toLowerCase()] === index + 1) { checker += 1; } }); return checker; }); return result; } ``` ### 厲害的解答 >1. `Array.map()` 以陣列的值為單位執行一次迴圈,所以它其實就像是 `Array.forEach()`,可以把 callback 想成 `Array.forEach() 的 {}(花括號,裡面放每一次迴圈的任務)` >2. 巢狀迴圈的概念==內迴圈全部跑完才會回頭執行外迴圈== > - 外迴圈:`Array.map()` > - 內迴圈:`Array.filter()` >3. 內迴圈執行完會得到陣列(因為 `Array.filter()`,就會執行打迴圈的任務 -> `Array.length` >4. `Array.map()` 的指責 => 回傳陣列 >5. `String.indexOf()` 可以得到字串的 `index`! ::: spoiler ```javascript= function solve(arr){ var alphabeth = "abcdefghijklmnopqrstuvwxyz"; return arr.map(x => x.toLowerCase().split('').filter((y,i) => i==alphabeth.indexOf(y)).length); } ``` ::: ## 10. 找出陣列中唯一的奇偶數 —— Find The Parity Outlier You are given an array (which will have a length of at least 3, but could be very large) containing integers. The array is either entirely comprised of odd integers or entirely comprised of even integers except for a single integer N. Write a method that takes the array as an argument and returns this "outlier" N. ```javascript= // Example [2, 4, 0, 100, 4, 11, 2602, 36] Should return: 11 (the only odd number) [160, 3, 1719, 19, 11, 13, -21] Should return: 160 (the only even number) ``` ### 我的方法 > 直接用 `Array.filter()` 找出奇偶數 > 陣列長度為 1 === 該輪 input 中只有那一個奇偶數 > 因為是陣列所以要用 `[0]` 取得值 ```javascript= function findOutlier(integers){ const evenList = integers.filter(isEven); const oddList = integers.filter(isOdd); return evenList.length === 1 ? evenList[0] : oddList[0]; } function isEven (number) { return number % 2 === 0; } function isOdd (number) { return number % 2 !== 0; } ``` ### 厲害的解答 > 題目有說陣列至少會給 3 個值 + 一個陣列內只有一個與其他不同 > 所以其實只要找前 3 個值就可以知道哪一個是獨特的了(如果前 3 個值只有一個是奇數,就拿那個奇數就好了,但如果全部都是奇數,就代表答案是其他最近的偶數) > - `Array.slice(0, 3) -> 切出前 3 個值` > - `Array.find(callBack) -> 從陣列內找出第一個符合 callBack 的值` ::: spoiler ```javascript= function findOutlier(integers){ return integers.slice(0,3).filter(even).length >=2 ? integers.find(odd) : integers.find(even); } function even(num){ return (num % 2 == 0); } function odd(num){ return !even(num) } ``` ::: ## 11. 走十步後會回到原點嗎? —— Take a Ten Minute Walk You live in the city of Cartesia where all roads are laid out in a perfect grid. You arrived ten minutes too early to an appointment, so you decided to take the opportunity to go for a short walk. The city provides its citizens with a Walk Generating App on their phones -- everytime you press the button it sends you an array of one-letter strings representing directions to walk (eg. ['n', 's', 'w', 'e']). You always walk only a single block for each letter (direction) and you know it takes you one minute to traverse one city block, so create a function that will return true if the walk the app gives you will take you exactly ten minutes (you don't want to be early or late!) and will, of course, return you to your starting point. Return false otherwise. ### 我的方法 1. 因為一步 = 一分鐘,題目限制一定要十分鐘,所以不是十分鐘的就直接錯誤 2. 算方位出現的次數,假如對應邊出現的次數都相同就代表走完後還是會回到原點 - 用 `Array.reduce()` 整理方向(以物件的方式整理) 3. 整理完後判斷對應的方位出現的次數是否相同(`n` 及 `s` 出現的次數必須相同,`w` 及 `e` 也是) ```javascript= function isValidWalk(walk) { if (walk.length !== 10) return false; const reduced = walk.reduce((accumulator, currentValue) => { if (accumulator[currentValue]) { accumulator[currentValue] ++; } else { accumulator[currentValue] = 1; } return accumulator; }, {}); if (reduced.n === reduced.s && reduced.w === reduced.e) return true else return false; } ``` ### 厲害的解答 如果以「篩選」為整理依據,也可以用 `Array.filter()`。也因為 `Array.filter()` 會回傳另一個陣列(其值為符合 callback 內的條件),就可以用 `array.length` 查看結果。 :::spoiler ```javascript= function isValidWalk(walk) { const north = walk.filter(item => { return item === "n" }).length; const south = walk.filter(item => { return item === "s" }).length; const east = walk.filter(item => { return item === "e" }).length; const west = walk.filter(item => { return item === "w" }).length; return walk.length === 10 && north === south && east === west; } ``` ::: ## 12. 輸入的兩個 string 為同構的 —— Check if two words are isomorphic to each other Two strings a and b are called isomorphic if there is a one to one mapping possible for every character of a to every character of b. And all occurrences of every character in a map to same character in b. Task In this kata you will create a function that return True if two given strings are isomorphic to each other, and False otherwise. Remember that order is important. Your solution must be able to handle words with more than 10 characters. Example True: ```javascript= CBAABC DEFFED XXX YYY RAMBUNCTIOUSLY THERMODYNAMICS ``` False: ```javascript= AB CC XXY XYY ABAB CD ``` 字串必須是一對一的關係: ```javascript= foo, bar -> false // f -> b // b -> f // o -> a // a -> o // o 先前已經對 a 了,有更動就不是同構的 // o -> r // r -> 0 ``` ### 我的方法 1. 同構代表長度一定也相同,先篩掉長度不同(`a.length !== b.length`) 2. 如果同構,代表同樣兩個字母一定彼此連結(比方來說 `a -> b` 及 `b -> a`),所以先用物件的方式保存配對 > 為什麼用物件? > 如果兩個字母互相連結,代表其中一個 key 必為另一個 property 的 value,所以跑先跑迴圈把配對建立起來 3. 檢查兩個物件的 key, value 及基準對應值(input 字串的關係) > 為什麼可以檢查得出來? > 透過物件 `[]`(object bracket notation)判斷建立的機制: > - 如果沒有該 property,就以對應的 input 字串為 key & value > - 如果有改 property,就檢查該 property 的 value 是否為對應基準值(input 字串) > - 如有不同就代表它們不是同構 ```javascript= function isomorph(a, b) { if (a.length !== b.length) return false; const tempCheckObj1 = {}; const tempCheckObj2 = {}; for (let i = 0; i < a.length; i ++) { // 沒有該 property 的話就先建立 // 以 index 為順序 // key name = 母 input value // value = 子 input value if (!tempCheckObj1[a[i]]) { tempCheckObj1[a[i]] = b[i]; } else if (tempCheckObj1[a[i]] !== b[i]) { return false; } if (!tempCheckObj2[b[i]]) { tempCheckObj2[b[i]] = a[i]; } else if (tempCheckObj2[b[i]] !== a[i]) { return false; } } return true; } ``` ```javascript= // 以 ABB, CDD 為例子 // 第一次 loop tempCheckObj1 = {A: C} tempCheckObj2 = {C: A} // 第二次 tempCheckObj1 = {A: C, B: D} tempCheckObj2 = {C: A, D: B} // 第三次 // 因為有 properties 所以要檢查 // B & D 互相檢查 tempCheckObj1 = {A: C, B: D} tempCheckObj2 = {C: A, D: B} ``` ### 厲害的解答 還在研究 ... 這題下次還要再做一遍,因為是我突然想到解法的 XD,為了測驗自己真的懂了。 ## 13. 連續出現三個數字時就以 `-` 隔開頭尾數 —— Range Extraction A format for expressing an ordered list of integers is to use a comma separated list of either individual integers or a range of integers denoted by the starting integer separated from the end integer in the range by a dash, '-'. The range includes all integers in the interval including both endpoints. It is not considered a range unless it spans at least 3 numbers. For example "12,13,15-17" Complete the solution so that it takes a list of integers in increasing order and returns a correctly formatted string in the range format. Example: ```javascript= solution([-10, -9, -8, -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]); // returns "-10--8,-6,-3-1,3-5,7-11,14,15,17-20" ``` ### 練習日期 2022/08/13 ### 我的方法 `for loop` 抓一次性的起始點,再有那層的 `while loop` 以該起始點往後檢查是否為連續三個數,並且透過 `Array.splice(startIndex, deleteCount)` 直接改變 `for loop` 的陣列(透過物件的 mutable 特性取得同樣的 reference),避免 `for loop` 重複執行那層 `while loop` 得到的 element。 1. 題目一定會給由小至大的陣列,所以可以先跳過排序的問題 2. 拿陣列的每一個值當作起點,並確診其後值(包含起點)是否為連續三個數字 > - 外層 `for loop` 抓陣列的每個 element 當作起點,每次的 `for loop` 都會執行一次 `while loop`。因為迴圈是裡面跑完才會跑外面的,所以要等到那層 `while loop` 結束後才會進到下一次 `for loop`。 > - 丟記錄型的變數,如果是連續的就更動記錄型變數的值。 3. `while loop` 確認連續的狀態,如果有連續就單純記錄,不管他直接進到下一次的 `while loop` 4. 達到三次,所以要 `頭-尾` 的 element > - 手動拼要的 element > - 刪除 `for loop` 移除的資料,避免 `for loop` 下一次還會以被移除的 element 執行迴圈 > - `Array.splice(startIndex, deleteCount)`,因為 index 會比實際的 length 還少一,所以 `deleteCount` 還要記得 + 1 5. 如果只有兩次的話還是要刪掉一個 element,避免 `for loop` 下一次的迴圈還會拿到 `while loop` 已經比對過的 element 6. 如果都不是的話就直接把該 element 推入 `result` 的陣列內,因為 `while loop` 沒有預先地先跑下個 element ```javascript= function solution(list){ const result = []; for (let i = 0; i < list.length; i++) { // 內層 while 的 index let indexTracker = i; // 記錄起點 let startIndex = i; let checker = true; // 記錄連續的次數 let times = 1; while (checker) { if (list[indexTracker + 1] === list[indexTracker] + 1) { times += 1; indexTracker += 1; } else { if (times >= 3) { const value = list[startIndex] + '-' + list[indexTracker]; result.push(value); list.splice(startIndex, indexTracker + 1 - startIndex, value); } else if (times === 2) { result.push(list[startIndex], list[indexTracker]); list.splice(startIndex, 1); } else if (times === 1) { result.push(list[indexTracker]); } checker = false; } } } return result.join(','); } ``` ### 厲害的解答 FP 的精彩範例!運用 high order function 的原理,以有名字的函式當作叫用 array methods 的引數。並透過 `Array.reduce()` 重新的到 `[[], []...]` 陣列(其 element 也是陣列,但有寫邏輯,限制該如何才可以 push element),後來再透過 `Array.map()` 轉換每個 element 的樣子,最後合併 element 成為字串。 > - `Array.reduce(callback(accumulator, currentValue), defaultValue)` 內 callback 的第一位為 `accumulator`,如果有給預設值第一次的 loop 則為預設值,如果沒有則為陣列的第一個 element。在第二次的 loop 之後 `accmulator` 則代表上一次 loop 回傳的結果。 :::spoiler ```javascript= function solution(individualIntegers) { return individualIntegers .reduce(splitIntoRanges, []) // array.map() 更改每個 element 的值(如果是連續三個數字的話就變成 '頭-尾') .map(convertToRange) // 最後拆回去成一個字串 .join(","); } // HOF,丟函式給 array methods function splitIntoRanges(ranges, number) { // 有給預設值,所以第一次的 loop ranges 代表 array.reduce 的預設值 [] if (!ranges.length) { // 直接推一個陣列 element 進去 ranges.push([number]); return ranges; } // 第二次 loop 之後就先抓上一個 element []... var lastRange = ranges[ranges.length - 1]; // 抓其最後一個數字 var lastNumber = lastRange[lastRange.length - 1]; // 如果下一個數字為連續數的話就直接推進抓出的 elememt[] 內,如果不是則是推一個新的 element[] 至上次回傳的結果 // 所以重頭到尾都是在更改上次回傳的結果([]) number === lastNumber + 1 ? lastRange.push(number) : ranges.push([number]); return ranges; } function convertToRange(range) { return range.length < 3 ? range.join(",") : range[0] + "-" + range[range.length - 1]; } ``` ::: ## 14. 一串句子內有使用到 26 個英文字母就可以 —— Detect Pangram A pangram is a sentence that contains every single letter of the alphabet at least once. For example, the sentence "The quick brown fox jumps over the lazy dog" is a pangram, because it uses the letters A-Z at least once (case is irrelevant). Given a string, detect whether or not it is a pangram. Return True if it is, False if not. Ignore numbers and punctuation. ### 練習日期 2022/08/13 ### 我的方法 1. 建立一個物件,以英文字母當作 key,其 value 為出現的次數 2. 先將句子切成一個單字(篩出空白),再將每一個單字切成一個個字母 3. `arr.forEach` 執行迴圈(切成一個個字母的陣列),並靠著字母當作 key 記錄次數 4. 取得記錄次數物件的值,並用 `arr.every` 確實檢查每個字母是否出現過 ```javascript= function isPangram(string){ const alphabet = { A: 0, B: 0, C: 0, D: 0, E: 0, F: 0, G: 0, H: 0, I: 0, J: 0, K: 0, L: 0, M: 0, N: 0, O: 0, P: 0, Q: 0, R: 0, S: 0, T: 0, U: 0, V: 0, W: 0, X: 0, Y: 0, Z: 0, }; const stringList = string.split(' '); stringList.forEach(str => { str.split('').forEach(s => { const key = s.toUpperCase(); if (alphabet[key] !== undefined) { alphabet[key]++; } }); }); return Object.values(alphabet).every(value => value > 0); } ``` ### 厲害的解答 原來 ES6 還有這麼多酷炫的招式...短短幾行就做完我的事情了 XDDD。 > - 用 ES6 的展開運算符(Spread Operator)可以直接等價於 `string.split('')` -> 切開 string 的每一個字,並組成一個陣列 > - 檢查字母是否都有被使用 1. 切好的陣列(其 element 為 a-z 的字母)執行 `array.every`,`array.every` 會跑完每個 element,如果全部 element 都符合 callback 就回傳 `true`,反之則回傳 `false` 2. 用 `str.includes(targetString)`,以句子為基底執行,透過這個方法可以得知這個句子是否包含 `targetString` 3. 結合 `array.every` 會跑全部 element 的特性 + `string.includes(targetString)` 得到結果,就直接完成我一開始「切割句子成字母 -> 迴圈取得字母 -> 迴圈內判斷是否有出現該字母 -> 最後還要檢查字母是否都有被使用」 :::spoiler ```javascript= function isPangram(string) { const alphabetList = [...'abcdefghijklmnopqrstuvwxyz']; return alphabetList.every((letter) => string.toLowerCase().includes(letter)); } ``` ::: ## 15. 字串每兩個為一個 element,不足需要補 _ —— [Split Strings](https://www.codewars.com/kata/515de9ae9dcfc28eb6000001) Complete the solution so that it splits the string into pairs of two characters. If the string contains an odd number of characters then it should replace the missing second character of the final pair with an underscore ('_'). ```javascript= * 'abc' => ['ab', 'c_'] * 'abcdef' => ['ab', 'cd', 'ef'] ``` ### 練習日期 2022/09/25 ### 我的方法 1. 建立一個空陣列用來存放 output 2. 建立一個變數存放迴圈的字串(之後這個字串就會變成閘門控制是否需要在 output 新增符合的字串) 3. 取得題目字串的每個字元 > 執行 `for loop` 讀取每個字元 > 讀取時順便拼存放的字串以及檢查該回合時是否需要洗掉保存的字串 > 當讀取最後一個字元以及存放的字串為單字元時,就必須要在該組字串加上底線 ```javascript= function solution(str){ let pair = ''; const result = []; for (let i = 0; i < str.length; i++) { pair += str[i]; if (pair.length === 2) { result.push(pair); pair = ''; } if (pair.length === 1 && i + 1 === str.length) { pair += '_'; result.push(pair); } } return result; } ``` ### 厲害的解答 只需要跑一次迴圈,並在 `for loop` 跳兩個字元: > - 只會有一維,且單向 > - 超取陣列的值不會報錯,只會得到 `undefiend` > - `index` 永遠比 `length` 少一 > - 所以從起始 0 出發,一次跳兩個,每一回合檢查該回合 `index + 1` 是否有值即可,無值就會得到 `undefiend`,代表就要給底線 :::spoiler ```javascript= function solution(str){ arr = []; for(var i = 0; i < str.length; i += 2){ second = str[i+1] || '_'; arr.push(str[i] + second); } return arr; } 'abc' // 第一回合(0) a -> 檢查 0 + 1 有沒有值 -> 有值就新增,無值補 _ // 第二回合(2) c -> 檢查 2 + 1 有沒有值 -> 有值就新增,無值補 _ // 第三回合(4) 沒符合執行條件,所以不會執行迴圈 'abcd' // 第一回合(0) a -> 檢查 0 + 1 有沒有值 -> 有值就新增,無值補 _ // 第二回合(2) c -> 檢查 2 + 1 有沒有值 -> 有值就新增,無值補 _ // 第三回合(4) 沒符合執行條件,所以不會執行迴圈 ``` :::