# [JavaScript] Array methods 整理資料的方法 ###### tags: `前端筆記` ## 1. `Array.find()` 找需要的那「第一」個東西 `Array.find(callBack)` 會 ==loop Array== 並且回傳第一個滿足 callBack 條件中的值,然後就停止 loop(這也是為什麼只會回傳「第一個」符合的值)。 如果 loop 完找不到就會回傳 `undefined`。 ```javascript= const arr1 = [3, 4, 5, 7, 8]; const a = arr1.find((value) => value > 5); // 7 ``` ![](https://i.imgur.com/UY6BV3X.png) *可以看到 `Array.find()` 會 loop 陣列的值* ### `Array.find()` 會 loop 陣列的值 知道 `Array.find()` 會 loop 陣列的值就可以做複雜的資料處理: [*ref.[Day 28 - 即時天氣] 保存與更新使用者設定的地區資訊 - localStorage 與 useEffect 的搭配使用*](https://ithelp.ithome.com.tw/articles/10228132) ```javascript= // 透過每個物件的 `cityName` 的值來找整個物件 const availableLocations = [ { cityName: '宜蘭縣', locationName: '宜蘭', sunriseCityName: '宜蘭', }, { cityName: '嘉義市', locationName: '嘉義', sunriseCityName: '嘉義', }, { cityName: '屏東縣', locationName: '恆春', sunriseCityName: '屏東', }, { cityName: '雲林縣', locationName: '雲林', sunriseCityName: '屏東', }, { cityName: '臺東縣', locationName: '臺東', sunriseCityName: '臺東', }, { cityName: '臺北市', locationName: '臺北', sunriseCityName: '臺北', }, { cityName: '金門縣', locationName: '金門', sunriseCityName: '金門', }, { cityName: '桃園市', locationName: '新屋', sunriseCityName: '桃園', }, { cityName: '彰化縣', locationName: '彰師大', sunriseCityName: '彰化', }, // { // cityName: '嘉義縣', // locationName: '阿里山', // sunriseCityName: '嘉義', // }, { cityName: '高雄市', locationName: '高雄', sunriseCityName: '高雄', }, { cityName: '基隆市', locationName: '基隆', sunriseCityName: '基隆', }, { cityName: '臺南市', locationName: '南區中心', sunriseCityName: '臺南', }, { cityName: '南投縣', locationName: '日月潭', sunriseCityName: '南投', }, { cityName: '臺中市', locationName: '臺中', sunriseCityName: '臺中', }, { cityName: '新竹縣', locationName: '新竹', sunriseCityName: '新竹', }, { cityName: '花蓮縣', locationName: '花蓮', sunriseCityName: '花蓮', }, { cityName: '連江縣', locationName: '馬祖', sunriseCityName: '馬祖', }, { cityName: '澎湖縣', locationName: '澎湖', sunriseCityName: '澎湖', }, { cityName: '新北市', locationName: '板橋', sunriseCityName: '新北市', }, ]; const found = (cityName) => { return availableLocations.find((location) => { return location.cityName === cityName; }); } console.log(found('新北市')); // { cityName: '新北市',locationName: '板橋', sunriseCityName: '新北市'} ``` ## 2. `Array.join(separator)` 將 Array 透過 `separator` 打掉合體吧 `Array.join(separator)` 可以把陣列拆掉以成一個字串,`separator` 則是控制把原先 array value 之間的 `,` 在合體成一個字串時變成以 `separator` 隔開。 ### 語法 ```javascript= join() join(separator) ``` - `separator` 會自動轉成字串 - 省略不寫 `separator` 會使用預設值 `,` ```javascritp= const a = [1, 2, 3]; a.join(); // '1,2,3' ``` - `separator` 為空字串 `''` 時會回傳一個中間沒有空白的字串 ```javascript= const a = [1, 2, 3]; a.join(''); // '123' ``` - `arr.join()` 會回傳已經 joined 的字串結果,但是不會更動原本的陣列 ```javascript= const a = [1, 2, 3]; a.join(''); // '123' console.log(a); // [1, 2, 3] ``` 使用範例: ```javascript= const a = [1, 2, 3]; console.log(a.join(' - ')); // '1 - 2 - 3 - 4' ``` ## 3. `Array.from()` 可以快速建立像是陣列的物件 因為昨天(2022/02/05)在解[這題](https://www.codewars.com/kata/576757b1df89ecf5bd00073b/solutions/javascript)的時候看到最佳解答有用這個,決定把它學習來。 ### 語法 ```javascript= // Arrow function Array.from(arrayLike, (element) => { /* ... */ } ) Array.from(arrayLike, (element, index) => { /* ... */ } ) // Mapping function Array.from(arrayLike, mapFn) Array.from(arrayLike, mapFn, thisArg) // Inline mapping function Array.from(arrayLike, function mapFn(element) { /* ... */ }) Array.from(arrayLike, function mapFn(element, index) { /* ... */ }) Array.from(arrayLike, function mapFn(element) { /* ... */ }, thisArg) Array.from(arrayLike, function mapFn(element, index) { /* ... */ }, thisArg) ``` ==第二個參數為 mapping function(該 function 第一個參數為 value,第二個為 index)。== 所以可以做到一些與 `Array.map()` 相同的事情: ```javascript= const test = Array.from([1, 2, 3], (x) => x * x); console.log(test); // [1, 4, 9] ``` 也可以直接創建有「長度(length)」的 Array-like Object(==注意只是看起來像是陣列的物件,但是它與陣列還是有些許差異==): ```javascript= // 我以前常用的 const arr1 = []; arr1.length = 3; console.log(arr1); // [empty × 3] for (let i = 0; i < arr1.length; i ++) { arr1[i] = i; } console.log(arr1); // [0, 1, 2] // ===== 使用 Array.from() ===== const arr2 = Array.from({length: 3}, (value, index) => index); // 可以直接在第一個參數用解構的方式定義出 Array-like Object 的長度 console.log(arr2); // [0, 1, 2] // ===== Array.from({length: x}) 解密 ===== const arr3 = Array.from({length: 3}); console.log(arr3); // [undefined, undefined, undefined] // 透過解構的方式定義長度,初始化的值會是 undefined ``` ## 4. `Array.filter()` + `Array.indexOf()` + `Array.lastIndexOf()` 找出陣列相同的值 假設有一陣列有若干的值,但是想要找出重複的值是什麼(1, 2),可以用 `Array.filter()`, `Array.indexOf()` 以及 `Array.lastIndexOf()` 的連續技,篩出重複的值。 ```javascript= const array = [1, 2, 3, 4, 1, 2]; const result = array.filter((value, index, arraySelf) => { return arraySelf.indexOf(value) !== index && arraySelf.lastIndexOf(value) === index; }); console.log(result) // [1, 2] ``` 思考的邏輯: * ==有重複就給我該值最後一次出現的== * `Array.filter(callback(element[, index[, array]])` 會迭代整個陣列的值 * `Array.indexOf()` 會回傳該值在陣列的 index(只會找第一個);`Array.lastIndexOf()` 會從尾開始找該值在陣列的 index(也只會找第一個) * 如果 `Array.indexOf(value)` === `Array.lastIndexOf(value)` 就代表該值沒有重複 * 反之 `Array.indexOf(value)` !== `Array.lastIndexOf(value)` 則代表該值有重複 * 因為只要該值在陣列最後一次出現的,所以 `Array.indexOf(value) !== index && Array.lastIndexOf(value) === index` 可以回傳最後一次出現的 -> 記得 `Array.filter()` 會對該陣列依照值從頭到尾跑迴圈 ## 5. Array.methods 的 paramter 是 callback,因此可以使用 function scope 的概念 參考自:[30 天精通 RxJS (03): Functional Programming 通用函式](https://ithelp.ithome.com.tw/articles/10186703) ```javascript= var courseLists = [{ "name": "My Courses", "courses": [{ "id": 511019, "title": "React for Beginners", "covers": [{ width: 150, height: 200, url: "http://placeimg.com/150/200/tech" }, { width: 200, height: 200, url: "http://placeimg.com/200/200/tech" }, { width: 300, height: 200, url: "http://placeimg.com/300/200/tech" }], "tags": [{ id: 1, name: "JavaScript" }], "rating": 5 }, { "id": 511020, "title": "Front-End automat workflow", "covers": [{ width: 150, height: 200, url: "http://placeimg.com/150/200/arch" }, { width: 200, height: 200, url: "http://placeimg.com/200/200/arch" }, { width: 300, height: 200, url: "http://placeimg.com/300/200/arch" }], "tags": [{ "id": 2, "name": "gulp" }, { "id": 3, "name": "webpack" }], "rating": 5 }] }, { "name": "New Release", "courses": [{ "id": 511022, "title": "Vue2 for Beginners", "covers": [{ width: 150, height: 200, url: "http://placeimg.com/150/200/nature" }, { width: 200, height: 200, url: "http://placeimg.com/200/200/nature" }, { width: 300, height: 200, url: "http://placeimg.com/300/200/nature" }], "tags": [{ id: 1, name: "JavaScript" }], "rating": 5 }, { "id": 511023, "title": "Angular2 for Beginners", "covers": [{ width: 150, height: 200, url: "http://placeimg.com/150/200/people" }, { width: 200, height: 200, url: "http://placeimg.com/200/200/people" }, { width: 300, height: 200, url: "http://placeimg.com/300/200/people" }], "tags": [{ id: 1, name: "JavaScript" }], "rating": 5 }] }]; /* var result = courseList 不得直接使用索引 covers[0],請用 concatAll, map, filter, forEach 完成 result 結果為 [ { id: 511019, title: "React for Beginners", cover: "http://placeimg.com/150/200/tech" }, { id: 511020, title: "Front-End automat workflow", cover: "http://placeimg.com/150/200/arch" }, { id: 511022, title: "Vue2 for Beginners", cover: "http://placeimg.com/150/200/nature" }, { id: 511023, title: "Angular2 for Beginners", cover: "http://placeimg.com/150/200/people" }, ] */ ``` 參考答案後的筆記: ```javascript= var courseLists = [{ "name": "My Courses", "courses": [{ "id": 511019, "title": "React for Beginners", "covers": [{ width: 150, height: 200, url: "http://placeimg.com/150/200/tech" }, { width: 200, height: 200, url: "http://placeimg.com/200/200/tech" }, { width: 300, height: 200, url: "http://placeimg.com/300/200/tech" }], "tags": [{ id: 1, name: "JavaScript" }], "rating": 5 }, { "id": 511020, "title": "Front-End automat workflow", "covers": [{ width: 150, height: 200, url: "http://placeimg.com/150/200/arch" }, { width: 200, height: 200, url: "http://placeimg.com/200/200/arch" }, { width: 300, height: 200, url: "http://placeimg.com/300/200/arch" }], "tags": [{ "id": 2, "name": "gulp" }, { "id": 3, "name": "webpack" }], "rating": 5 }] }, { "name": "New Release", "courses": [{ "id": 511022, "title": "Vue2 for Beginners", "covers": [{ width: 150, height: 200, url: "http://placeimg.com/150/200/nature" }, { width: 200, height: 200, url: "http://placeimg.com/200/200/nature" }, { width: 300, height: 200, url: "http://placeimg.com/300/200/nature" }], "tags": [{ id: 1, name: "JavaScript" }], "rating": 5 }, { "id": 511023, "title": "Angular2 for Beginners", "covers": [{ width: 150, height: 200, url: "http://placeimg.com/150/200/people" }, { width: 200, height: 200, url: "http://placeimg.com/200/200/people" }, { width: 300, height: 200, url: "http://placeimg.com/300/200/people" }], "tags": [{ id: 1, name: "JavaScript" }], "rating": 5 }] }]; /* var result = courseList 不得直接使用索引 covers[0],請用 concatAll, map, filter, forEach 完成 result 結果為 [ { id: 511019, title: "React for Beginners", cover: "http://placeimg.com/150/200/tech" }, { id: 511020, title: "Front-End automat workflow", cover: "http://placeimg.com/150/200/arch" }, { id: 511022, title: "Vue2 for Beginners", cover: "http://placeimg.com/150/200/nature" }, { id: 511023, title: "Angular2 for Beginners", cover: "http://placeimg.com/150/200/people" }, ] */ // Array.prototype.map = function(callback) { // const result = []; // this.forEach(function(item) { // result.push(callback(item)); // }); // return result; // } // Array.prototype.filter = function(callback) { // const result = []; // this.forEach(function(item) { // callback(item) && result.push(item); // }); // return result; // } // 攤平 array // [[]] -> []... Array.prototype.concatAll = function() { const result = []; this.forEach(function(array) { result.push(...array); }); return result; } // methods 裡面是放 callback,所以自然就可以用函式的 scope 取得 variable const test = courseLists .map((list) => list.courses .map((item) => item.covers .filter((cover) => cover.width === 150) .map((x) => { return { // item 是第二個 map callback 中的 variable // x 是上一個 filter 後 [{..}, {...}, {...}] 中 map 每一次 loop 的 element id: item.id, title: item.title, cover: x.url } }))).concatAll().concatAll(); // 攤平第一個及第二個 map 回傳的 array console.log(test); ``` ## 6. 將 firebase 自動生成的 cryptic string 變成資料的 id 因為 firebase 會自動生成一組獨特的 cryptic string,並且會影響資料結構為 `cryptic string: { ...data }`。為了保存 firebase 提供的 cryptic string 做為每一筆資料的 id,可以這麼做: ![](https://hackmd.io/_uploads/rkHE9ST69.png) ![](https://hackmd.io/_uploads/B13E5Ba69.png) ```javascript= const transformedData = (data) => { const resultArr = []; for (const key in data) { resultArr.push({ ...data[key], id: key}); } return resultArr; } ``` 但其實可以用 `Object.entries` + `Array.map()` 變更資料結構: ```javascript= const transformedData = (data) => { // Object.entries(Object) -> 會得到陣列 [[objectKey, objectValue]] // 再用 Array.map() + 解構,重新組合一個新的陣列解構 [{ content, id}...] return Object.entries(data).map(([id, content]) => { return {...content, id }; }); } ``` 因為 `Objecy.entries(Object)` 確保回傳的陣列內每個 element 都是陣列(第一位為 key,第二位為 value),所以用 `Array.map()` + 解構(原本 element 的結構為:`[key, {value}]`)的時候就不用擔心解構的順序會不會隨機。 ## 參考資料 1. [Day2-陣列操作常用的20個函式](https://ithelp.ithome.com.tw/articles/10215864) 2. MDN