JS Day 28: Array Methods - 修改原始陣列的方法

有 N 人看过
  • In-place modification (修改原始陣列)
    • copyWithin()
    • fill()
    • pop()
    • push()
    • reverse()
    • shift()
    • unshift()
    • sort()
    • splice()

sort() 排序

sort() 方法會原地(in place)對一個陣列的所有元素進行排序,並回傳此陣列。排序不一定是穩定的(stable)。預設的排序順序是根據字串的 Unicode 編碼位置(code points)而定。 — MDN web docs

驗證:

const array = ['你','我','他'];
array.sort(); 
// => Array(3) [ "他", "你", "我" ]

找出各個元素的 code points

array.map(item => item.codePointAt());
// => Array(3) [ 20182, 20320, 25105 ]

其實 [ "他", "你", "我" ] 就是按照 各個元素的 code points [ 20182, 20320, 25105 ]來排序的。

基本上不大需要這樣的排序方式,所以會傳入一個 comparison function 比較函式來進行排序

arr.sort( compareFunc(a,b) {
    return a - b; // ascending order
});

如果比較函式回傳小於 0 (returns less than zero): a 排在 b 前面

如果比較函式回傳 0 (returns zero): a 與 b 位置保持不變

如果比較函式回傳大於 0 (returns greater than zero): b 排在 a 前面

const nums = [9, 2, 88, 1000, 348];
nums.sort( (a, b) => a - b);
// 結果 => Array(5) [ 2, 9, 88, 348, 1000 ]

執行順序:

  1. a - b

    9 - 2 = 7 (大於 0, b 排在 a 前面)
    nums = [2, 9, 88, 1000, 348];

  2. a - b
    9 - 88 = -79 (小於 0, a 排在 b 前面)
    nums = [2, 9, 88, 1000, 348];

  3. a - b
    88 - 1000 = -912 (小於 0, a 排在 b 前面)
    nums = [2, 9, 88, 1000, 348];

  4. a - b
    1000 - 348 = 652 (大於 0, b 排在 a 前面)
    nums = [2, 9, 88, 348, 1000];

排序:

const ascendingOrder  = arr.sort( () => a - b); // 升序
const descendingOrder = arr.sort( () => b - a); // 降序

練習 (Sorting object array):

JavaScript30 - 04 Array Cardio Day 1

JS 28

References:

[JavaScript] 從 Array 的 sort 方法,聊到各瀏覽器的實作,沒想到 Chrome 和FireFox 的排序如此不同

splice() 刪除/加入

splice() 方法可以藉由刪除既有元素並/或加入新元素來改變一個陣列的內容。 — MDN web docs

array.splice( 起始索引, 欲刪除元素數量, 欲增加的元素)

範例:

// 增加 1 個元素,刪除 0 個元素

let fruits = ['酪梨','桃子','香蕉','葡萄','西瓜'];
fruits.splice(3, 0, '番茄'); 
// output => [ "酪梨", "桃子", "香蕉", "番茄", "葡萄", "西瓜" ]

   0      1     2     3     4
['酪梨','桃子','香蕉','葡萄','西瓜'] 
// 在第 3 個位置加入 '番茄'

   0      1     2     + 3 +     4      5
['酪梨','桃子','香蕉', '[番茄]', '葡萄','西瓜'] 

// 增加 0 個元素,刪除 2 個元素

 let fruits = ['酪梨','桃子','香蕉','葡萄','西瓜'];
fruits.splice(2, 2);  // 從索引 2 開始,刪除兩個元素 [ "香蕉", "葡萄" ]
// output => ['酪梨','桃子','西瓜']

// 增加 4 個元素,刪除 4 個元素

let fruits = ['酪梨','桃子','香蕉','葡萄','西瓜'];
fruits.splice(1, 4, '九層塔', '花椰菜', '紅蘿蔔', '高麗菜');
// 從索引 1 算起,刪除四個元素 [ "桃子", "香蕉", "葡萄", "西瓜" ]
// output => [ "酪梨", "九層塔", "花椰菜", "紅蘿蔔", "高麗菜" ]

練習:

JS 28 (Terry Yu) - 把 id 206 的物件移動到陣列索引 0(第一個)

const terryElement = data.find(p => p.id == 206); // return 物件
const terryIndex = data.findIndex(p => p.id == 206); // return 索引
data.splice(terryIndex, 1); // 從索引x刪除一個元素
data.splice(0, 0, terryElement); // 從索引 0 加入 terryElement 並刪除 0 個元素

unshift(), shift(), push(), pop() 這四個算是比較好理解的,但是名稱取得很容易讓人搞混(除了 push)。下方的連結都在講解 stack and queue 可以更深入了解這四個方法。

JavaScript Arrays as Queue and Stack
Stacks and Queues, Simplified
堆疊(Stack) & 佇列(Queue)
你其實不用在 JavaScript 實作一個 Queue

unshift() 方法會添加一個或多個元素至陣列的開頭,並且回傳陣列的新長度。 — MDN web docs

// unshift()
let arr = [1, 3, 5, 7, 9]
arr.unshift('你', '我', '他');  // 新增 3 個元素到陣列前

unshift 被刻意設計為具通用性;此方法可以藉由 called 或 applied 應用於類似陣列的物件上。若欲應用此方法的物件不包含代表一系列啟始為零之數字屬性序列長度的 length 屬性,可能是不具任何意義的行為。 — MDN web docs

MDN 這部分的描述不太明白,之後會再補上。

shift() 移除第一個元素

shift() 方法會移除並回傳陣列的第一個元素。此方法會改變陣列的長度。 — MDN web docs

// shift()
let fruits = ['香蕉', '酪梨', '鳳梨', '葡萄'];
fruits.shift(); // 移除第一個元素 ”香蕉“

fruits.shift(2); // 參數 2 沒有作用,只有第一個元素會被移除
console.log(fruits); // => ["鳳梨", "葡萄"]

Push() 在末端加入元素

push() 方法會添加一個或多個元素至陣列的末端,並且回傳陣列的新長度。 — MDN web docs

// push()
let colors = ['red', 'pink', 'orange'];

colors.push(); // 沒有作用,陣列長度維持一樣
console.log(colors.length);
colors.push('yellow'); // 新增 'yellow' 在末端
console.log(colors.length);
colors.push(['black','white']); // 傳入一個陣列在末端
console.log(colors);

Pop() 在末端移除元素

pop() 方法會移除並回傳陣列的最後一個元素。此方法會改變陣列的長度。 — MDN web doc

// pop()
let fruits2 = ['香蕉', '酪梨', '鳳梨', '葡萄'];
fruits2.pop(); // 移除最後一個元素 '葡萄'

fruits2.pop(2); // 參數 2 沒有作用,但最後一個元素會被移除
console.log(fruits2); // => ["香蕉", "酪梨"]

CodePen.io: unshift(), shift(), push(), pop()

copyWithin() 陣列內複製元素

copyWithin() 方法會對陣列的一部分進行淺拷貝(shallow copies)至同一陣列的另一位置並回傳此陣列,而不修改其大小。 — MDN web doc

將一個陣列裡的某一個元素複製到同陣列的某一索引並覆蓋掉。

// copyWithin(終點索引, 被複製的索引, 如沒有輸入會複製到最後);
let foods = ['🍞', '🍔', '🍟', '🍕', '🍙', '🍳', '🍦'];
foods.copyWithin(1, 5); 
// 從索引 5 到最後一個元素 🍳, 🍦
// 覆蓋掉索引 1 & 2 🍔, 🍟
// ['🍞', '🍳', '🍦', '🍕', '🍙', '🍳', '🍦'];

let foods2 = ['🍞', '🍔', '🍟', '🍕', '🍙', '🍳', '🍦'];
foods.copyWithin(0, 4, 5); // 複製索引 4 到 5 個元素 🍙,🍳 到索引 0

fill() 在陣列裡指定的位置覆蓋上靜態值

// fill()
let phil = [1, 2, 3, 4];
phil.fill('i', 1, 3) // 從索引1到索引2 (不包含第3)
// => [1, 'i', 'i', 4]

reverse() 把陣列順序顛倒

// reverse()
let num = ['一', '二', '三'];
num.reverse(); // => ['三', '二', '一'];

References:

The Modern JavaScript Bootcamp (2020)

MDN web docs

本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。