---
# System prepended metadata

title: CH3-U12 ：陣列的批次操作(map/forEach/filter...)

---

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. 壓縮物件與轉換(抽取必要資料產生新物件)**

![](https://i.imgur.com/gk5raPv.png)

```
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的商品
> 過濾名稱含有特定關鍵字的商品
> 是否所有商品都在特價中
