跳至主要内容

[JS] JavaScript 陣列(Array)

Array 的所有方法 @ MDN

TL;DR

Array.isArray(obj)                      // 檢驗是否為陣列
const arrCopy = [...arr] // 複製陣列(copy array)@ airbnb 4.3
const nodes = [...arrayLike] // 將 array-like(例如 NodeList) 轉成 array @ airbnb 4.4

/**
* 建立陣列
**/
let arr = [element0, element1, ..., elementN] /* Good */
let arr = new Array(element0, element1[, ...[, elementN]]) /* Bad */
let arr = new Array(arrayLength)

/**
* 原陣列不會被改變 (non-mutating) 的方法
**/
arr.at(-1) // 取出陣列的最後一個元素,可以用來替換 arr[arr.length-1] 的寫法
arr.with(<index>, <value>) // 把元素為 index 的資料換成 value
arr.map( callback<element> ) // 用來將陣列中的元素做轉換(computed),搭配 return
arr.forEach( callback<item, index, array> ) // 用來疊代陣列中的元素,並執行動作,不需搭配 return
arr.slice(begin, end) // 用來擷取陣列中的部分元素(不包含 end)
arr.filter( callback<item, index, array> ) // 若 callback return 為 true 時則保留該元素
arr.reduce( callback<accumulator, currentValue, currentIndex, array>, initialValue ) // 搭配 return ,return 的內容會進到 accumulator,最後回傳 accumulator
arr.concat(value1, [value2], ...) // 把陣列連接在一起

// 根據 callback 的規則篩選,需在 callback 中搭配 return 使用
arr.some( callback<item, index, array> ) // 檢驗陣列中是否有符合該 callback 規則的元素,有的話回傳 true
arr.every( callback<item, index, array> ) // 檢驗陣列中所有元素是否都符合 callback 規則,有的話回傳 true
arr.find( callback<item, index, array> ) // 檢驗陣列中是否有符合該 callback 規則的元素,有的話回傳第一個找到的元素值
arr.findIndex( callback<item, index, array> ) // 檢驗陣列中是否有符合該 callback 規則的元素,有的話回傳第一個找到的 index

// 根據 target 篩選
arr.includes(target, fromIndex) // 檢驗陣列中是否包含 target 這個 element,有的話回傳 true
arr.indexOf( target, fromIndex ) // 返回找到該元素的第一個 index 值,若找不到則回傳 -1

/**
* 原陣列會被改變(mutation method)
**/
arr.pop() // 刪除最後一個元素
arr.push() // 將元素塞入陣列中
arr.shift() // 刪除陣列中第一個元素
arr.splice(start, deleteCount, newItem) // 用來移除陣列中的部分元素,並可補入新元素
arr.sort(compareFunction) // 根據 compareFunction 來將陣列重新排序
arr.copyWithin(target, start, end)

/**
* 其他
*/
arr.join('<str>') // 將陣列以 <str> 連接成字串,預設是","

Array 的所有方法 @ MDN

Snippets and Utilities

判斷式否為陣列(型別判斷)

keywords: Array.isArray()
Array.isArray([]); // true
Array.isArray([1]); // true
Array.isArray(new Array()); // true

Array.isArray(); // false
Array.isArray({}); // false
Array.isArray(null); // false
Array.isArray(undefined); // false

將 array-like 的 Node List 轉成 Array

// rails/activestorage/app/javascript/activestorage/helpers.js
function toArray(value) {
if (Array.isArray(value)) {
return value;
} else if (Array.from) {
return Array.from(value);
} else {
return [].slice.call(value);
}
}

從陣列中隨機抽取一個元素(sample element in array)

var rand = myArray[Math.floor(Math.random() * myArray.length)];

Getting a random value from a JavaScript array @ StackOverflow

建立一個長度為 n 且內容相同的陣列

/**
* 建立一個長度為 length ,且所有內容均為 'element' 的陣列
* create an array with same element in given length
**/
Array(_length_).fill(_element_);
Array(10).fill('ele');
// [ 'ele', 'ele', 'ele', 'ele', 'ele', 'ele', 'ele', 'ele', 'ele', 'ele' ]

建立一個元素從 1 ~ n 的陣列

使用 Array(5) (或 new Array(5))可以產生一個帶有 5 個 empty 元素的陣列,透過解構賦值可以讓這些元素變成 undefined

Array(5)               // [empty × 5]
[...Array(5)] // [undefined, undefined, undefined, undefined, undefined]
[...Array(5).keys()] // [0, 1, 2, 3, 4]

Array(5).keys() // Array Iterator {}

若不是單純要從 0 開始,則可以使用 Array.from() 搭配第二個參數 mapFn 把值填入:

let array1to5 = Array.from({ length: 5 }, (val, index) => index + 1); // [1, 2, 3, 4, 5]
let array1to5 = Array.from(new Array(5), (val, index) => index + 1); // [1, 2, 3, 4, 5]

new Array(1)Array(1) 的本質上是一樣的,參考 Stack Overflow

去除陣列中重複的元素(keep unique element)

keywords: unique element
let arr = ['aaron', 'po', 'aaron', 'po', 'chung'];
let arrKeepUniqueElements = [...new Set(arr)];

篩選出最小值

keywords: min, max

// 可以用來篩選陣列最小值/最大值
let minX = arr.reduce((pre, cur) => Math.min(pre.x, cur.x));

let max = arr.reduce((acc, curr) => Math.max(acc, curr));

陣列的解構賦值(Array Destructing)

基本使用

let [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1, 2, 3

搭配 spread syntax (...)使用

let destructing_array = function () {
let allPeople = ['Aaron', 'John', 'Andy', 'Hat', 'Candy'];
let [a, b, c, ...d] = allPeople;
console.log(a, b, c, d); // Aaron John Andy ["Hat", "Candy"]
};

賦予預設值

let [a, b, c = 4, d = 'Hello'] = [1, 2, 3];
console.log(a, b, c, d); // 1, 2, 3, "Hello"

陣列解構賦值實際應用

/**
* 提取陣列元素 @ airbnb 5.2
**/
const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;

陣列的基本方法

let arr = ['Taipei', 'Taiwan', 'Aaron'];
console.log(arr.length); // 取得陣列元素數目

/**
* 會改變原本的陣列內容
**/
let arr_pop = arr.pop(); // 用來刪除最後一個元素
console.log(arr); // ["Taipei", "Taiwan"]
console.log(arr_pop); // 'Aaron'

let arr_push = arr.push('Apple'); // 用來將元素塞入陣列中
console.log(arr); // ["Taipei", "Taiwan", "Apple"]
console.log(arr_push); // 3

let arr_shift = arr.shift(); // 刪除第一個元素
console.log(arr); // ["Taiwan", "Apple"]
console.log(arr_shift); // "Taipei"

/**
* 不會改變原本的陣列內容
**/
let arr_map = arr.map((item) => item + 's'); // 將陣列中的每個元素執行某函式,但不改變原來的 arr
console.log(arr); // ["Taipei", "Taiwan", "Apple"]
console.log(arr_map); // ["Taipeis", "Taiwans", "Apples"]

避免改變原陣列(immutable)

// 使用 Array.prototype.slice 來避免改變原陣列

// 為陣列添加元素
const addElement = (originArray) => {
return [...originArray, 0];
};

// 移除陣列內的元素
const removeElement = (originArray, index) => {
return [...originArray.slice(0, index), ...originArray.slice(index + 1)];
};

// 改變陣列內的元素
const modifyElement = (originArray, index) => {
return [...originArray.slice(0, index), originArray[index] + 1, ...originArray.slice(index + 1)];
};

陣列常用方法說明

使用展開運算符(spread syntax)

keywords: 連結陣列, concat, insert, clone, copy
// 連結陣列 joining arrays
const odd = [1, 3, 5];
const arr_concat = [2, 4, 6].concat(odd);
const arr_spread = [2, 4, 6, ...odd]; // [2, 4, 6, 1, 3, 5]

// 陣列插值 insert array anywhere
const arr_insert = [2, 4, ...odd, 6]; // [2, 4, 1, 3, 5, 6]

// 複製陣列 cloning array, copy array, clone array
const arr = [1, 2, 3, 4];
const arr2 = [...arr]; // [1, 2, 3, 4]

Array.prototype.map(callback<item, index, array>)

/**
* var new_array = arr.map(callback<item, index, array>)
* 需要使用到陣列,並回傳回原陣列時,所以在 callback 中會有 return
**/
var numbers = [1, 5, 10, 15];
var roots = numbers.map(function (x) {
return x * 2;
});
// roots is now [2, 10, 20, 30]
// numbers is still [1, 5, 10, 15]

Array.prototype.forEach(callback[, thisArg])

/**
* arr.forEach(callback<currentValue, index, array> [, thisArg]);
* 需要使用到陣列,但不需要回傳陣列時使用(根據這個陣列自己做一些事)
**/

let people = [
{ name: 'Aaron', country: 'Taiwan' },
{ name: 'Jack', country: 'USA' },
{ name: 'Johnson', country: 'Korea' },
];

people.forEach((person) => {
console.log(`${person.name} lives in ${person.country}`);
});

// 特殊簡寫
let users = ['Aaron', 'Jack', 'Johnson'];
function getUserName(username) {
console.log(username);
}

users.forEach(getUserName);

/* 上面這行是下面這段的簡寫
users.forEach( user =>{
getUserName(user)
})
*/

Array.prototype.slice()

主要功能:擷取陣列中的部分元素

/**
* arr.slice(),
* arr.slice(begin)
* arr.slice(begin, end)
* slice() 方法將陣列的一部分淺拷貝,
* 回傳從 begin 開始到 end 結束(不包括 end)新陣列。
* 原始陣列不會被修改。
**/

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
arr.slice(0, 3); // [0,1,2]
arr.slice(3, 7); // [3,4,5,6]

// 最後一個是 -1
arr.slice(-3, -1); // [8,9]
arr.slice(-5, -3); // [6,7]
arr.slice(-3, -5); // []

// 如果要擷取到最後一個
arr.slice(-5, arr.length); // [6,7,8,9,0]

Array.prototype.splice()

主要功能:移除陣列元素(並放入新陣列元素) 注意事項:splice( ) 的用法會改變原陣列結構,且原本的 arr 和 arrSplice 會輸出不同內容。

/**
* array.splice(start)
* array.splice(start, deleteCount)
* array.splice(start, deleteCount, item1, item2, ...)
*
* deleteCount:要移除的陣列個數,省略表示 arr.length - start
* item1, item2:可用來從 start 添加陣列
**/

arr = [1, 3, 5, 6];
arr_splice = arr.splice(2, 1); // 表示把第 2 個元素從陣列中移除
console.log(arr); // 保留下來的元素,回傳 [1, 3, 6]
console.log(arr_splice); // 被移除掉的元素,回傳 [5]
arr_splice = arr.splice(2, 1, 'new'); // 把第 2 個元素從陣列移除,並放進新元素"new"

Array.prototype.sort([compareFunction])

預設是根據 Unicode 字串碼位排序。

  • 若 compareFunction(a, b) 小於 0, 將 a 排在比 b index 還小處, i.e. a 排在第一個(a, b 不改變位置順序)。
  • 若 compareFunction(a, b) 回傳 0, a 與 b 互相不會改變順序, 但會與全部其他元素比較排列(a, b 不改變位置順序)。
  • 若 compareFunction(a, b) 大於 0, 將 b 排在比 a index 還小處(a, b 位置互換)。
/**
* arr.sort(compareFunction)
* 會改變原陣列
**/

arr = [4, 6, 3, 3, 6, 8, 12];

function compareNumbers(a, b) {
// console.log(a, b)
return a - b; // 由小排到大
// return b - a // 由大排到小
}
arr.sort(compareNumbers);
console.log(arr); // [3, 3, 4, 6, 6, 8, 12]
/**
* 根據屬性值排序,但是屬性值不能有空的情況
**/

var arr = [
{ name: 'Edward', value: 21 },
{ name: 'Sharpe', value: 37 },
];

function compareObj(a, b) {
return a.value - b.value;
}
arr.sort(compareObj);
console.log(arr);

Array.prototype.filter( [begin], [end] )

/**
* arr.filter(callback<element, index, array> [, thisArg])
* 若回傳值為true則將當前的元素保留; 若是false,則刪除該元素。
**/

// 把所有小於 10 的資料都移除
function isBigEnough(value) {
return value >= 10;
}
var filtered = [12, 5, 8, 130, 44].filter(isBigEnough); // filtered is [12, 130, 44]

Array.prototype.indexOf()

/**
* arr.indexOf(searchElement[, fromIndex = 0])
* indexOf() 方法返回在數組中可以找到給定元素的第一個索引,如果不存在,則返回-1
**/

Array.prototype.some( )

/**
* 用來檢驗陣列中是否包含某規則的元素
* 會針對陣列中的每個元素去執行 some 裡面的 callback ,如果有 true 就停止,沒有就到最後回傳 false
* arr.some(callback<element, index, array> [, thisArg] )
*/

function isBiggerThan10(element, index, array) {
return element > 10;
}

[2, 5, 8, 1, 4].some(isBiggerThan10); // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true

Array.prototype.reduce(callback, [initialValue])

/**
* 建議要給起始值
* arr.reduce( callback<accumulator, currentValue, currentIndex, array>, initialValue )
* 搭配 return 使用,return 的內容會傳到 accumulator
**/

// 基本使用
let arr = [0, 1, 2, 3, 4];

let arrAfterReduce = arr.reduce((accumulator, item, currentIndex) => {
return accumulator + item; // return 的值會進入 accumulator
}, 0);

console.log('afterReduce ' + arrAfterReduce); // 10

// 可以用來篩選陣列小值
let minX = arr.reduce((pre, cur) => {
return pre.x < cur.x ? pre.x : cur.x;
});

reduce() 搭配 push()concat() 使用時,要注意 push() 會改變原陣列,回傳的是新陣列的長度(length),concat() 才會回傳的是被連結後的結果。

/* 取得陣列中放在物件裡的值 */
let arr = [{ name: 'A' }, { name: 'B' }, { name: 'C' }];

let sum = arr.reduce((acc, cur, i, arr) => {
/* 使用 push */
acc.push(cur.name); // 回傳陣列長度
return acc; // 原陣列改變

/* 使用 concat */
// return acc.concat(cur.name) // 回傳連結後的陣列
}, []);

console.log(sum); // [ 'A', 'B', 'C' ]

Array.prototype.concat(value1[, value2])

/**
* 用來將陣列連接起來
* arr.concat(value1 [, value2])
* 和 push 有一個差異是在 push 會改變原本的陣列,
* 但是 concat 不會改變原本的陣列
**/

let arr = [1, 2, 3];
let arrAppend = [4, 5, 6];
arr.concat(7, arrAppend); // [1, 2, 3, 7, 4, 5, 6],arr 不會改變

Array.prototype.fill(value, start, end)

建立一個長度為 length ,且所有內容均為 'element' 的陣列(Create an array with the same element)

/**
* Array(<length>).fille(<element>)
**/
Array(3).fill(4); // [4, 4, 4]

Array.prototype.at(index)

過去 JavaScript 沒辦法使用 arr[-1] 這種方式來取得 Array 中的最後一個元素,比較常見的方法是要用 arr[arr.length - 1]

有了 .at() 後,它可以接受負整數,因此可以直接用 arr.at(-1),就可以得到陣列中最後一個元素的值。

Array.prototype.with(index, value)

當我們要修改陣列中特定 index 的元素時,可以使用 bracket notation,例如 arr[3] = 'value',但這麼做算是 mutation 的做法,也就是會改到原本的陣列。

如果我們希望能改陣列中特定 index 的元素值,且希望是 immutable(不會改到原陣列的),那麽就可以使用 .with(index, value) 這個方法,例如,arr.with(3, 'value')

之所以要叫 with 可以想像成:creating a new array "with" a specific element replaces or updated。

Array.prototype.copyWithin(target, start, end)

copyWithin(target, start, end) 這個方法是在不改變原陣列長度的情況下,複製陣列中的元素到其他位置。

  • start 開始複製到 end - 1 的位置
  • 把複製的內容從 target 位置貼上
  • start 沒給時,預設會是 0
  • end 沒給時,預設會是拿到最後一個
const arr = [0, 1, 2, 3, 4];
arr.copyWithin(2, 0); // [0, 1, 0, 1, 2],複製整個 array,並從第 2 個 element 位置開始貼上
arr.copyWithin(0, 3); // [3, 4, 2, 3, 4],end 沒給時,預設時拿到最後一個

Style Guide

建立陣列

// good
const arr = [
[0, 1],
[2, 3],
[4, 5],
];

const objectInArray = [
{
id: 1,
},
{
id: 2,
},
];

const numberInArray = [1, 2];

常見問題

參考資料