# (穎旻試教版本)第二堂 - 進階陣列操作:掌握 filter 篩選技巧
# 課程大綱
1. forEach 練習
1. 1 個觀念 - 20min
2. 介紹 filter 觀念
1. 1 個觀念 - 25min
3. 試著用 forEach、filter 協助解題
1. 1 個觀念- 25min
4. 旅遊套票練習
1. 1 個觀念 - 25min
# 1. forEach 練習
```jsx
let data = [
{
"name": "肥宅心碎賞櫻3日",
"area": "高雄",
"price": 1400,
"rate": 10
},
{
"name": "貓空纜車雙程票",
"area": "台北",
"price": 240,
"rate": 2
},
{
"name": "台中谷關溫泉會1日",
"area": "台中",
"price": 1765,
"rate": 7
}
];
```
## 問答
1. 用 forEach 計算所有套票的總價
2. 用 forEach 計算評價在 7 以上的套票
3. 高雄有幾筆資料
4. 撰寫第五週作業 LV1
**作業等級表**
LV1:拿[CSS 現成模版](https://codepen.io/hexschool/pen/BaQveVm?editors=1010)套用,練習下方的套票列表用 JS 的 innerHTML 套用,並需運用 Code JS 面板裡面的 data 變數資訊,上方新增套票功能與下拉篩選地區功能可不做。
# 2. 介紹 filter 觀念
forEach vs filter
| 差異點 | `forEach` | `filter` |
| --- | --- | --- |
| **用途** | **遍歷**陣列中的每一個元素 | **篩選**陣列中符合條件的元素 |
| **回傳值** | **沒有回傳**值 (回傳 `undefined`) | **回傳**一個新的陣列 |
| **是否改變原陣列** | 不會改變原陣列 | 不會改變原陣列 |
| **用例** | 僅執行操作,不需要篩選或回傳新陣列 | 篩選出符合條件的元素並回傳新陣列 |
### 電商訂單情境
1. **發送通知**:向每位顧客發送 `console.log` 通知,確認訂單已經處理
2. **篩選大額訂單**:需要找出金額超過 $100 的訂單,看看誰是大戶
```jsx
// 訂單清單
const orders = [
{ id: 1, amount: 50, customer: 'Alice' },
{ id: 2, amount: 150, customer: 'Bob' },
{ id: 3, amount: 200, customer: 'Charlie' },
{ id: 4, amount: 80, customer: 'David' }
];
// 1. 使用 forEach 發送通知
// 2. 使用 filter 篩選大額訂單
console.log('大額訂單清單:', highValueOrders);
// filter 回傳一個新的陣列,其中包含符合條件的訂單
// 小題目,以下 orderConsole 變數的值是?
// 1. undefined
// 2. 全部 console 的總和
const orderConsole = orders.forEach(function(order) {
console.log('通知顧客 ' + order.customer + ':訂單 ' + order.id + ' 已處理');
});
```
### 小技巧教學: debugger
```jsx
const orders = [50, 150, 200, 80];
const highValueOrders = orders.filter(function(amount) {
debugger; // 這行會在瀏覽器的開發者工具中暫停程式執行
return amount > 100;
});
```
1. 遍歷陣列:從陣列的第一個元素開始,逐個檢查每個元素。
2. 應用條件函式:對於每個元素,會將該元素作為參數傳遞給指定的條件函式。這個條件函式**返回一個布林值**,表示是否將該元素包含在最終的篩選結果中。
3. 篩選元素:**如果條件函式返回`true`,則該元素將被保留在結果陣列中**;如果返回**`false`**,則該元素將被排除在外。
4. 回傳結果陣列:當所有元素都遍歷完畢後,**`filter()`**方法**返回一個新的陣列**,其中包含所有符合條件的元素,而原始陣列不受影響。
# 3. 試著用 forEach、filter 協助解題
- forEach:**遍歷**陣列中的每一個元素
- filter:**篩選**陣列中符合條件的元素
## 電商廣告通路評估
| 廣告通路 | 廣告支出 (spend) | 廣告收入 (revenue) | 投資回報率 (ROAS) = revenue/spend |
| --- | --- | --- | --- |
| Google Ads | 10,000 | 35,000 | 3.5 |
| Facebook Ads | 8,000 | 24,000 | 3.0 |
| LinkedIn Ads | 5,000 | 12,000 | 2.4 |
| Twitter Ads | 4,000 | 10,000 | 2.5 |
| Instagram Ads | 6,000 | 15,000 | 2.5 |
## 表格解釋
- **廣告通路**:各種不同的廣告平台。
- **廣告支出 (spend)**:花出去的廣告費。
- **廣告收入 (revenue)**:透過廣告賺來的營業額。
- **投資回報率 (ROAS)**:收入與支出的比值,表示每花費一單位的廣告費所帶來的收益
## 六角 2022 程式體驗營,臉書廣告數據為例
![image](https://hackmd.io/_uploads/HJ3CfIBhC.png)
## roas 效益介紹
1. roas 在 1 以下:廣告效益差,可能會決定不投
2. roas 在 2~3:差強人意,列入評估。但在電商零售業或許會繼續投遞
3. roas 在 5~10 左右:效益不錯,六角學院廣告通常會盡可能在這個區間
## 題目設計
```jsx
const marketingChannels = [
{ name: 'Google Ads', spend: 10000, revenue: 35000, roas: 3.5 },
{ name: 'Facebook Ads', spend: 8000, revenue: 24000, roas: 3.0 },
{ name: 'LinkedIn Ads', spend: 5000, revenue: 12000, roas: 2.4 },
{ name: 'Twitter Ads', spend: 4000, revenue: 10000, roas: 2.5 },
{ name: 'Instagram Ads', spend: 6000, revenue: 15000, roas: 2.5 }
];
```
### 尋找高效行銷通路
1. 請設計一段程式碼來找出 ROAS(投資回報率)超過 3 的渠道,並將這些通路放入一個新陣列中
2. 請設計一段程式碼計算所有通路的總支出和總收入
# 4. 旅遊套票練習
1. **實作:**
- 示範使用陣列方法結合條件運算符來實現地區篩選。
https://codepen.io/AliceChiang/pen/NWmgReN?editors=1111
```html
<select name="areaSelections" id="areaSelections">
<option value="全部">全部</option>
<option value="台中">台中</option>
<option value="台北">台北</option>
<option value="高雄">高雄</option>
</select>
```
簡易寫法:
```jsx
const data = [
{
id: 0,
area: "台中"
},
{
id: 1,
area: "台中"
},
{
id: 2,
area: "台北"
},
{
id: 3,
area: "高雄"
}
]
const areaSelections = document.querySelector('#areaSelections');
let filterData = []
areaSelections.addEventListener('change', () => {
if (areaSelections.value === '全部') {
filterData = data; // 如果選擇的是「全部」,則返回所有資料
} else {
filterData = data.filter((item) => areaSelections.value === item.area);
} // console.log(filterData)
})
```
**更好的程式碼:**
- 改使用 filter
- 運用 clean code 的概念,修改成更好的程式碼結構、命名規則等
**進階寫法:**
```jsx
const ticketList = [
{
id: 0,
area: "台中"
},
{
id: 1,
area: "台中"
},
{
id: 2,
area: "台北"
},
{
id: 3,
area: "高雄"
}
]
const allAreas = '全部';
const areaSelections = document.querySelector('#areaSelections');
const selectedArea = areaSelections.value;
const filterTicketsByArea = (selectedArea) => {
return ticketList.filter((ticket) => selectedArea === allAreas || selectedArea === ticket.area);
};
areaSelections.addEventListener('change', () => {
const areaFilterData = filterTicketsByArea(selectedArea)
// console.log(areaFilterData)
})
```
**變數命名:對應 Clean Code 原則「命名:使用具有意義且可閱讀的名稱」、「使用可解釋的變數」**
- data → ****ticketList
- filterData → areaFilterData
- item **→** ticket
**提取變數:對應 Clean Code 原則「使用可搜尋的名稱」**
- const allAreas = '全部';
areaSelections.value === '全部' → areaSelections.value === allAreas
- const selectedArea = areaSelection.value;
**獨立函式:對應 Clean Code 原則「一個函式只做一件事情」**
- filterTicketsByArea