National Chengchi University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

1

Local AI

--

Uedu Code

--

政治大學 AQI 58 25°C PM2.5 12
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 09

陣列

陣列的特點、常用方法與 map、filter、reduce
Interaction實作 25 minute(s)

Learning Objectives

  • 建立陣列,用索引與 length 存取元素
  • 用 push、pop、shift、unshift、splice 改變陣列,用 slice、join、indexOf、includes 取得新的值
  • 用 forEach、map、filter、reduce、find 處理每一個元素
  • 用比較函式正確排序數字與物件
  • 說明陣列是參照,並用展開語法或 slice 複製陣列

為什麼需要陣列

要記錄三位同學的數學成績,可以宣告三個變數:

let aMath = 100;
let bMath = 90;
let cMath = 60;

但如果有 50 位同學,就要 50 個變數,要加總、找最高分都得一個一個寫。陣列(array)把一群值放在同一個名稱底下, 用索引(index)分辨:

const scores = [100, 90, 60];
scores[0];        // 100:索引從 0 開始
scores.length;    // 3:元素的個數
  • 陣列是有順序的:索引 0 是第一個,length - 1 是最後一個
  • 元素可以是任何型別,同一個陣列裡也可以混合(但實際使用時,通常一個陣列放同一種資料)
  • 搭配迴圈,不管有幾個元素,程式都一樣長

建立陣列一般用 [ ];new Array('Red', 'Green') 也可以,但 new Array(3) 會建立「3 個空位」而不是 [3],容易搞混,所以很少用。

索引、length 與搜尋

注意第 7 行指定 colors[3] 之後陣列變長;第 9 行把 length 改成 2,後面兩個元素就不見了。indexOf 找不到時回傳 -1。

加入、取出與 splice

方法 作用 回傳
push(x, …) 加到最後面(可以一次加好幾個) 新的長度
pop() 取出最後一個 被取出的元素
unshift(x, …) 加到最前面 新的長度
shift() 取出第一個 被取出的元素
splice(start, 刪幾個, 要放入的…) 從 start 刪掉幾個元素,再放入新的元素 被刪掉的元素組成的陣列

這五個方法都會改變原本的陣列。

splice 一個方法就能刪除、插入或取代:

a.splice(5, 1);         // 刪掉索引 5 的一個元素
a.splice(2, 0, 99);     // 刪 0 個:在索引 2 插入 99
a.splice(5, 3, 20, 30); // 刪掉 3 個,換成 20、30

舊教材會提到 delete a[1]:它不會把元素移除,只會在那個位置留下空位,length 不變,所以不要用它刪陣列元素。

push、pop、shift、unshift

每一步看右邊 fruits 的變化。push 回傳的是新的長度(3),pop 和 shift 回傳被取出的元素。

splice 改變原陣列,slice 不會

對照第 3 行的 splice 和第 9 行的 slice:splice 執行後右邊的 a 改變了;slice 產生新的陣列 part,a 不變。

想一想:陣列的方法

1.

下面的程式會印出什麼?

const stack = [1, 2, 3];
const x = stack.pop();
stack.push(9);
console.log(x, stack);
2.

下面的程式會印出什麼?

const letters = ['a', 'b', 'c', 'd', 'e'];
console.log(letters.slice(1, 3));
3.

下面的程式會印出什麼?

const a = [10, 20, 30, 40];
const r = a.splice(1, 2);
console.log(a.length, r.length);
第 1 次作答

把函式交給陣列:forEach、map、filter、reduce、find

這些方法都收一個函式當引數,陣列會對每個元素呼叫一次這個函式(這種被別人呼叫的函式叫 callback)。 callback 依序收到 (元素, 索引, 整個陣列),用不到的參數可以不寫。

方法 callback 回傳什麼 方法本身回傳 原陣列
forEach(fn) 不需要 undefined 不變
map(fn) 新的值 每個元素換成回傳值的新陣列(長度相同) 不變
filter(fn) true/false 回傳 true 的元素組成的新陣列 不變
find(fn) true/false 第一個回傳 true 的元素;沒有就是 undefined 不變
findIndex(fn) true/false 第一個符合的索引;沒有就是 -1 不變
reduce(fn, 初始值) 累計到目前的結果 最後的累計結果 不變

reduce 的 callback 收到 (累計值, 元素):第一次的累計值是你給的初始值,之後每一次是上一次的回傳值。

[1, 2, 3].reduce((sum, n) => sum + n, 0);
// 第 1 次:sum = 0, n = 1 → 1
// 第 2 次:sum = 1, n = 2 → 3
// 第 3 次:sum = 3, n = 3 → 6

上面的 s => s >= 60 是箭頭函式,第 11 課〈函式〉會詳細介紹;現在先把它讀成「收到 s,回傳 s >= 60」。

filter、map、reduce、find 與 forEach

每呼叫一次 callback,右邊就會多出一個「(箭頭函式)」的框架,裡面的 s 是這一次拿到的元素,框架下方會顯示這一次的回傳值。注意 find 找到 85 之後就停了,不會再看 99。

sort:排數字一定要給比較函式

sort() 不給引數時,會把每個元素轉成字串再依字元順序排序。所以 [10, 1, 5, 100].sort() 會得到 [1, 10, 100, 5]: 字串 '100' 的第一個字元 '1' 比 '5' 小。

排數字要傳入比較函式 (a, b) => …,sort 會拿兩個元素問它:

比較函式回傳 意思
負數 a 排在 b 前面
正數 b 排在 a 前面
0 兩個順序不變

所以 (a, b) => a - b 是由小到大,(a, b) => b - a 是由大到小。排物件時比較它們的某個屬性,例如 (p, q) => p.age - q.age。

sort 和 reverse 都會改變原陣列。不想改到原陣列時,先複製再排序([...nums].sort(…)); 較新的瀏覽器(2023 年起各主要瀏覽器都已支援)也可以用 nums.toSorted(…),它回傳排好的新陣列,原陣列不變。

數字排序與物件排序

第 5 行的 sort() 沒有比較函式,排出來的順序很奇怪;第 8 行加上 (a, b) => a - b 之後,每一次比較都會出現一個框架,你可以看到 a、b 是哪兩個元素,以及回傳值的正負。

陣列也是參照

陣列是物件的一種,所以和上一課的物件一樣:const arr2 = arr1 只是讓兩個變數指向同一個陣列,透過哪一個變數修改,兩邊都看得到。

要複製一份新的陣列,常用:

const copy1 = [...arr1];        // 展開語法
const copy2 = arr1.slice();     // slice 不給引數:從頭複製到尾
const copy3 = Array.from(arr1);

這些都是淺複製:如果元素本身是物件,新陣列裡放的仍是同一個物件的參照(第 10 課會說明深複製)。

走訪陣列用一般的 for(需要索引時)或 for…of(只需要值時),或是 forEach。不要用 for…in:它拿到的是索引字串, 還可能走訪到陣列上自訂的其他屬性。

指定與複製

看右邊的箭頭:arr1、arr2 指向同一個陣列;arr3、arr4 各自是新的陣列。

想一想:callback 與排序

1.

下面的程式會印出什麼?

console.log([10, 1, 5, 100].sort());
2.

下面的程式會印出什麼?

const nums = [1, 2, 3, 4];
const result = nums.filter(n => n % 2 === 0).map(n => n * 10);
console.log(result, nums.length);
3.

下面的程式會印出什麼?

const prices = [30, 50, 20];
const total = prices.reduce((sum, p) => sum + p, 100);
console.log(total);
第 1 次作答

實作:找出及格的同學

下面是十位同學的分數。請完成三件事:用 filter 找出超過 60 分的分數、印出共有幾位、用 reduce 算出全班平均(保留一位小數,用 toFixed(1))。

你的輸出
預期輸出

重點精華回顧

  • 陣列是有順序的一組值,用 [ ] 建立;索引從 0 開始,length 是元素個數,最後一個是 arr[arr.length - 1]。
  • push/pop 處理最後面,unshift/shift 處理最前面,splice 可以刪除、插入、取代;這些方法都會改變原陣列。
  • slice 取出一段成為新陣列、join 接成字串、indexOf 找索引(找不到是 -1)、includes 回答有沒有。
  • forEach 逐一處理、map 轉換、filter 篩選、find 找第一個、reduce 累計;它們都不改變原陣列。
  • sort() 預設依字串排序,排數字要寫 sort((a, b) => a - b);sort 會改變原陣列。
  • 陣列是參照;要複製用 [...arr] 或 arr.slice()。走訪陣列用 for、for…of 或 forEach,不要用 for…in。
這一課完成了!
Next lesson:JSON 與複製
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。