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