# [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
```

*可以看到 `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,可以這麼做:


```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