hahow 網頁程式入門
# CH3-U12 :陣列的批次操作(map/forEach/filter...)
* 批次操作的方法
* Array **.forEach** (函數) ------- | 對每個元素執行動作
* Array **.map** (函數) ----------- | 執行結果存到新陣列
* Array **.filter**(函數) ----------- | 留下符合條件的元素
* Array **.find** (函數) ------------| 找到第一個符合條件的元素
* Array **.some** (函數) ---------- | 判斷有元素符合條件
* Array **.every** (函數) ---------- | 判斷所有元素都符合條件
* Array **.sort** (函數) ------------ | 根據大小排列陣列
* Array **.reduce** (函數,初始值) --| 根據規則縮減陣列
## Array **.forEach** (函數)
> 將陣列元素每個都套用函數執行後"放回陣列"中
> 重點:變動會回到本體array的元素
**ex.**
```
books.forEach(function(book){ //引數名稱自訂(代表array裡面的單一元素)
book.total=book.discount*book.price
console.log(book.total)
})
console.log(books)
//陣列中會新增一個total屬性資料
```
## Array **.map** (函數)
> 這邊的map 作為"映射;映對"之意
> 陣列元素抓出來function執行後會使用return回傳
> 回傳結果組成"新的陣列"
> 原有的陣列會被保留(與forEach不同)
**ex 1. 數字陣列**
```
var numbers=[1,2,3,4,5]
var new_array=numbers.map(function(num){
return num*num
})
console.log(new_array) //[1,4,9,16,25]
```
**ex 2. 物件陣列**
```
var new_array = items.map(function (item) {
return item.newvalue = item.value * 5;
//item.newvalue表示一組新的屬性資料
});
console.log(new_array); //一組含有newvalue屬性資料的新陣列
console.log(items); //舊陣列被保留
```
**ex 3. 壓縮物件與轉換(抽取必要資料產生新物件)**

```
var datas=[
{
name:"test",
price: 50,
discount:0.8,
tags: ['happy']
},{
name:.....
...
}]
var newArray=datas.map(function(obj){
return {
name: obj.name,
total: obj.price*obj.discount
}
})
console.log(datas)
console.log(newArray)
//新陣列萃取出name,total兩個屬性的資料
```
## Array **.filter** (函數)
> 根據"條件判斷"後的"回傳值(true/false)",
> 決定要不要將元素複製到"新陣列"
> filter 過濾 > 只濾出符合條件的資料
```
var numbers=[1,2,3,4,5]
var new_array=numbers.filter(function(num){
return num>3 //只回傳num>3的元素
})
console.log(new_array) //[4,5]
```
## Array **.find** (函數)
> 根據判斷結果回傳值(true/false)
> 傳回 **"第一個"** 符合條件的"元素" ( 非陣列! )
**ex.**
var fruits = ["banana", "orange", "papaya"];
var find_fruit=fruits.find(function(fruit){
return fruit.indexOf("an")>-1 //回傳>-1的值 (表示回傳資料含有"an"字串)
(indexOf()原理:回傳的值代表array或string的起始位置,
若該資料不存在,回傳的值為 -1
})
console.log(find_fruit)
//"banana"
回傳"第一個"符合的資料
(orange為第二筆符合 > 不回傳)
* .indexOf()用法
```
範例 1
var fruits = ["banana", "orange", "papaya"];
console.log(fruits.indexOf("papaya"));
//2 (陣列中第2個位置的資料)
範例 2
var fruitname = "babans&grapes";
console.log(fruitname.indexOf("gr"));
//7 (string中第7個位置開始)
console.log(fruitname.indexOf("ge"));
//-1 (不存在的值會顯示-1)
```
## Array **.some** (函數)
> "其中"有元素符合條件回傳true
**ex 1. 數字陣列**
var numbers=[1,2,3,4,5,6]
//當其中有偶數回傳 true,否則false
var some_even=numbers.some(function(num){
return (num % 2 == 0) //餘數=0
})
console.log(some_even) //true
**ex 2. 文字陣列**
var fruits = ["banana", "orange", "papaya"]
var some_fruit=fruits.some(function(fruit){
return fruit.indexOf("an")>-1
})
console.log(some_fruit) //true
## Array **.every** (函數)
> "每一個"元素符合條件回傳true
>
**ex 1. 數字陣列**
var numbers=[1,2,3,4,5,6]
當所有均為奇數回傳 true,否則false
var every_odd=numbers.every(function(num){
return num%2==1 //餘數=0
})
console.log(every_odd) //false
**ex 2. 文字陣列**
var fruits = ["banana", "orange", "papaya"]
var every_fruit=fruits.every(function(fruit){
return fruit.indexOf("an")>-1
})
console.log(every_fruit) //false
## Array **.sort** (函數)
* #### [技術文件說明](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) [進一步的範例說明](https://hackmd.io/DhuraNACQfCIYeXTeF53cw?view)
> 根據回傳值重新"排序"陣列 (由小到大;由大到小;字串的排序...)
> 原本的陣列會被直接修改,不會產生新陣列
> **由大到小** or **由小到大** 排列,取決 **return** 的值
* 原理:arr.sort( **[compareFunction]** )
* function compare **(a,b)** 的引數代表的位置為 **[b,a]**
* 若compareFunction為比較函數,為"升序 or 位置不變"
```
//假設一組原始陣列如下
var arr = [2, 74, 84, 213, 124, 58, 424, 1111];
//印出a,b引數,可得在陣列中:b 於 a 的前面[b,a]
arr.sort(function(a,b){
console.log(a,b)
})
// 74 2
// 84 74
// 213 84
// 124 213
// 58 124
// 424 58
// 1111 424
```
* **compareFunction的形式如下:**
結果: 若無指定function return為a-b或b-a則結果為升序或不變
```
function compare(a,b){
if(a>b){ //即a-b>0
return 1 //b排在小於a索引的位置(即b前a後)[b,a]
//由於b<a 所以結果為"升序"
}
if(a<b){ //即a-b<0
return -1 //a排在小於b索引的位置(即a前b後)[a,b]
//由於a<b 所以結果為"升序"
}
return 0 //a=b時
//位置不變[b,a]
}
```
* 如果 compareFunction 沒有被應用,元素將被轉換為**字串**並以 **Unicode 編碼位置**進行比較來排序。
```
var arr = [2, 74, 84, 213, 124, 58, 424, 1111];
console.log(arr.sort())
//[1111,124,2,213,424,58,74,84] //一個字元一個字元比較,由小到大排序
```
* compareFunction **由大到小** or **由小到大** 排列,取決 **return** 的值
```
1. //由小到大的排序: return a-b
function compare(a,b){
return a-b //[b,a]中 a-b>0的結果即 a大b小
}
---------------------------------------------------------
2. //由大到小的排序: return b-a
function compare(a,b){
return b-a //[b,a]中 b-a>0的結果即 b大a小
}
```
**ex.應用範例1.**
```
var arr = [2, 74, 84, 213, 124, 58, 424, 1111];
//升序:由小到大的function
function toSmall(a,b){
return a-b
}
//降序:由大到小的function
function toLarge(a,b){
return b-a
}
arr.sort(toSmall)
console.log(sort) //[2,58,74,84,124,213,424,1111]
arr.sort(toLarge)
console.log(sort) //[1111,424,213,124,84,74,58,2]
```
**ex.應用範例2.**
```
var members=[{year:27},{year:77},{year:47},{year:63}]
members.sort(function(a,b){
return b.year-a.year //b-a>0(=true)代表b>a,
因此b位置的值>a位置的值
(由大到小排列)
})
console.log(members) //原本的陣列直接被修改後套用
//[{"year": 77},{"year": 63},{"year": 47},{ "year": 27}]
```
## Array **.reduce** (函數,初始值)
reduce 壓縮;減少
* #### [技術文件說明](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
> 概念:有帶 **"暫存器"** 的forEach
> 暫存器:允許帶入一個暫存的變數做累計運算,最後回傳結果(常見計算總價)
* 原理:假設 reduce() 以下例方式使用:
```
[0, 1, 2, 3, 4].reduce(
function (
accumulator, //暫存器
currentValue, //陣列所要迭代處理中的元素
currentIndex, //目前所迭代處理中的元素之索引
array //呼叫 reduce() 方法的陣列
) {
return accumulator + currentValue;
} //沒給初始值的話,暫存器會帶入陣列中第[0]個元素;
迭代處理中的元素則由陣列中第[1]個元素開始
);
```
* **以範例說明原理**
```
var arr=[2,1,3,4]
```
* **沒給起始值,迭加的暫存器**
```
var reduce_arr2=
arr.reduce(function (accumulator,currentValue,currentIndex,array) {
console.log ("暫存器:"+accumulator+', 迭代值:'+currentValue+', 陣列位置:'+currentIndex,array)
return accumulator+currentValue
}) //沒給原始值,暫存器(acc)一開始會直接帶入arr[0]的值,
//迭代處理中的元素(cur)會自動由arr[1]的值開始
console.log(reduce_arr2) //10
----------------------------------------------------
console
"暫存器:2, 迭代值:1, 陣列位置:1" [2,1,3,4]
"暫存器:3, 迭代值:3, 陣列位置:2" [2,1,3,4]
"暫存器:6, 迭代值:4, 陣列位置:3" [2,1,3,4]
10
```
* **給起始值的暫存器**
```
var reduce_arr1=
arr.reduce(function (accumulator,currentValue,currentIndex,array) {
console.log ("暫存器:"+accumulator+', 迭代值:'+currentValue+', 陣列位置:'+currentIndex,array)
return accumulator //暫存器沒有加總任何訊息,因此每次都式套用起始值0
},0) //有給起始值0
console.log(reduce_arr1) //最終跑出來的結果為0
----------------------------------------------------
console
"暫存器:0, 迭代值:2, 陣列位置:0" [2,1,3,4]
"暫存器:0, 迭代值:1, 陣列位置:1" [2,1,3,4]
"暫存器:0, 迭代值:3, 陣列位置:2" [2,1,3,4]
"暫存器:0, 迭代值:4, 陣列位置:3" [2,1,3,4]
0
```
* **給起始值,且每次迭加的暫存器**
```
var reduce_arr3=
arr.reduce(function (accumulator,currentValue,currentIndex,array) {
console.log ("暫存器:"+accumulator+', 迭代值:'+currentValue+', 陣列位置:'+currentIndex,array)
return accumulator+1
},0) //有給起始值,暫存器從"起始值"開始代入
//second call時 acc+1(=0+1=1)...以此類推
//有給起始值,currentValue會從arr[1]的值開始代入
console.log(reduce_arr3) //印出retuen最後的結果 4
----------------------------------------------------
console
"暫存器:0, 迭代值:2, 陣列位置:0" [2,1,3,4]
"暫存器:1, 迭代值:1, 陣列位置:1" [2,1,3,4]
"暫存器:2, 迭代值:3, 陣列位置:2" [2,1,3,4]
"暫存器:3, 迭代值:4, 陣列位置:3" [2,1,3,4]
4
```
**ex.**
```
books=[{price: 10},{price:20},{price:30}]
var total=books.reduce(function(total,a){ //total為暫存變數,a為陣列中的obj
return total+a.price
//total為每次累加的暫存變數
//a.price為每個obj要累加的值
},0) //0為total的初始值
console.log(total) //0+10+20+30=60
```
## 箭頭函式
* #### [技術文件說明](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
* **ex1.使用 reduce 的箭頭函數**
```
[0, 1, 2, 3, 4].reduce( (prev, curr) => prev + curr );
//func的引數 //return的結果
```
* 有給第二個參數(起始值)的箭頭函數
```
[0, 1, 2, 3, 4].reduce(
(accumulator, currentValue, currentIndex, array) => { //箭頭前的括弧接引數
return accumulator + currentValue; //箭頭後為執行內容
},
10 //起始值
);
```
* **ex2. 使用 map 的箭頭函數**
```
const materials = [
'Hydrogen',
'Helium',
'Lithium',
'Beryllium'
];
console.log(materials.map(material => material.length));
// Expected output: Array [8, 6, 7, 9]
與下方同
---------------------------------
console.log(materials.map(function(material){
return material.length
}))
```
## 課堂練習
原始:使用for回圈跑過每個數
```
var numbers=[1,2,3,4,5,6]
for(var i=0;i<numbers.length;i++){
console.log(numbers[i]) //印出每一個值
}
```
#### .forEach() 批次印出
```
numbers.forEach(function(number){
console.log(number)
})
```
#### .map() 修改陣列內容套用新陣列
```
var result=numbers.map(function(number){
return number*number
console.log(result)
//[1,4,9,16,25,36]
console.log(numbers)
//[1,2,3,4,5,6]
```
#### .filter() 過濾原本的元素
```
var result2=numbers.filter(function(number){
return number%2==0 //只會return偶數,若要return奇數則number%2==1
})
console.log(result2)
//[2,4,6]
```
#### .find() 找出第一個符合的元素
```
var result3=numbers.find(number){
return number>3
}
console.log(result3)
//4
若沒有符合條件的元素可以回傳
var result3=numbers.find(number){
return number>7 //沒有大於7的值
}
console.log(result3)
//undefined
```
#### .every() 判斷是否為部分符合條件
```
var result4=numbers.every(function(number){
return number>0
})
console.log(result4)
//true
--------------------------------------
var result5=numbers.every(function(number){
return number>4
})
console.log(result5)
//false
```
#### .some() 判斷是否為全部符合條件
```
var result5=numbers.some(function(number){
return number>4
})
console.log(result5)
//true
--------------------------------------
var result5=numbers.some(function(number){
return number>7
})
console.log(result5)
//false
```
#### .sort() 排序
```
var numbers=[1,5,3,2,4]
numbers.sort(function(a,b){
return a-b
})
console.log(numbers)
//[1,2,3,4,5]
```
#### .reduce() 暫存器運迭加算
```
var total=
numbers.reduce(function(total,num){ //第一個為暫存器,第二個為現在的元素
return total+num //注意不是total+=num(暫存器已經有每次迭加的功能)
},0) //初始值,代表暫存器由total=0起始
console.log(total)
//15
------------------------------------------
var total=
numbers.reduce(function(total,num){
return total+num
},"") //初始值為"空字串"
console.log(total)
//"12345" //字串的累加
```
```
相乘的範例
var total=
numbers.reduce(function(total,num){
return total+num*num
},0)
console.log(total)
//120 //1*2*3*4*5
```
### 物件陣列資料
```
var acts=[
{
name:"聖誕節派對",
attend: 40,
price: 200,
cost: 7000
},{
name:"平面設計演講",
attend: 20,
price: 500,
cost: 9000
},{
name:"新年登山看日出派對",
attend: 60,
price: 100,
cost: 1000
}
]
```
#### .forEach() 批次印出
```
acts.forEach(function(act,index){ //第2個引數可以是陣列中的位置,也可簡稱i
console.log(index+":"+act.name)
})
```
#### .map() 轉換物件,計算虧損
```
var acts2=acts.map(function(act){
return {
name: act.name,
total: act.attend*act.price-act.cost
}
})
console.log(acts2) //新的物件陣列
console.log(act) //原始的資料不變
```
#### .filter() 過濾出符合條件的活動
```
var acts3=acts.filter(function(act){
return act.price<=200
})
console.log(acts3) //只會輸出票價符合<=200的活動物件
```
#### .find() 第一個符合條件的活動
```
var acts4=acts.find(function(act){
return act.price<=200
})
console.log(acts4) //只會輸出"第一個"票價符合<=200的活動物件
```
```
var party=acts.find(function(act){
return act.name.indexOf("派對")!=-1 //不等於-1(只有在indexOf找不到值的時候會回傳-1)
})
console.log(party)
//找到第一個含有"派對"字樣的物件陣列'
```
#### .reduce() 迭加物件陣列中元素
```
var datas=[{n:1},{n:2}]
var total=
datas.reduce(function(total,num){
return total*num.n
},1)
console.log(total)
//2 //1(起始值)*1*2
```
#### .every() 判斷每個都符合條件的活動
```
var people = [
{name: "Frank",gender: "male", year: 23},
{ name: "jan", gender: "male", year: 25 },
{ name: "bob", gender: "male", year: 30 },
{ name: "tom", gender: "male", year: 18 }
]
var allmale=people.every(function(person){
return person.gender=="male"
})
console.log(allmale)
//true
```
### 箭頭語法
```
var years=people.map(function(person){
return person.year
})
console.log(years)
//[23,24,30,18]
箭頭語法如下
----------------------------------
var years=people.map((person)=>{
return person.year
})
----------------------------------
大括號及return可省略
var years=people.map((person)=>person.year)
```
```
只有一個引數時,引數的括弧也可省略
var years=people.map(person=>person.year*person.year)
console.log(years)
//[529,625,900,324]
```
```
箭頭語法輸出字串長度
var texts= ["124124",'dfg3h3','dsf']
console.log(texts.map(text=>text.length))
//[6,6,3]
```
```
執行的結果較多時,要保留大括號
var texts= ["124124",'dfg3h3','dsf']
var newText=texts.map(text=>{
var temp="" //定義空字串
for(var i=0;i<text.length;i++){ //跑過每個texts中的text的文字內容
temp+='o' //套用temp=temp+o(代表有幾個字元就會有幾個o)
}
return temp
})
console.log(newText)
```
## 課後練習
**[codepen](https://codepen.io/Chien-C/pen/RweWLbP?editors=0012)**
> PCHOME節錄商品資料
> 使用sort排列價格,過濾價格<500的商品
> 過濾名稱含有特定關鍵字的商品
> 是否所有商品都在特價中