陣列是一種最為常見和應用廣泛的資料結構, 不用語言的陣列不太一樣, 對於 js 的陣列來說, 和 python 的列表極其類似, 都是可動態擴充值和型別的, 還是來大致總結一下這個基礎資料結構.
建立陣列
let arr1 = new Array('a', 'b', 66)
let arr2 = ['a', 'b', 66]
console.log(arr1, arr2);
陣列訪問和迭代
- 遞迴
- for 索引
- for / of 值
const fibArr = []
fibArr[1] = 1
fibArr[2] = 1
for (let i = 3; i < 20; i++) {
fibArr[i] = fibArr[i-1] + fibArr[i-2]
}
for (let i = 1; i < fibArr.length; i++) {
console.log(fibArr[i]);
}
console.log(fibArr);
更多場景是我們直接遍歷陣列元素, 類似的結構是: arr = [ {}, {}, {} ...]
推薦用 for ... of
const arr = [1, 2, 3, {'name': 'cj', age: 18}]
for (let item of arr) {
console.log(item);
}
PS F:\algorithms> node .\array.js
1
2
3
{ name: 'cj', age: 18 }
新增元素
- push
- unshift
- Array.prototype. xxx = function (value) =>
let arr = []
// 尾插
arr.push(666)
arr[arr.length] = 888
console.log(arr);
// 頭插
Array.prototype.insertHead = function(value) {
for (let i = this.length; i >= 0; i--) {
this[i] = this[i-1]
}
this[0] = value
}
arr.unshift(2, 3)
arr.insertHead(111)
console.log(arr);
PS F:\algorithms> node .\array.js
[ 666, 888 ]
[ 2, 3, 111, 666, 888 ]
刪除元素
- pop
- shift
- Array.prototype.xxx = function(value) =>
let arr = [1, 2, 3, 4, 'cj', 'nb']
// 尾刪
item = arr.pop()
// 頭刪
item2 = arr.shift()
console.log(item, item2, arr);
nb 1 [ 2, 3, 4, 'cj' ]
當然從頭部刪除也可以透過如下兩個步驟:
- 建立一個
reIndex
的方法, 接收一個陣列, 然後過濾掉undefined
的元素, 返回新陣列 - 透過索引方式手動刪除原陣列第一個元素, 然後將這個結果丟到
reIndex
中即可
// 將陣列中的中 undefined 元素過濾
Array.prototype.reIndex = function (arr) {
const newArr = []
for (let i = 0; i < arr.length; i++) {
if (arr[i] != undefined) newArr.push(arr[i])
}
return newArr
}
// 手動刪除第一個元素並排序
Array.prototype.removeHead = function() {
for (let i = 0; i < this.length; i++) {
this[i] = this[i+1]
}
return this.reIndex(this)
}
let arr = [1, 2, 3, 4, 'cj', 'nb']
console.log(arr.removeHead());
[ 2, 3, 4, 'cj', 'nb' ]
在任意位置新增或者刪除元素
- splice (start, deleteCount, item1, item2, ...))
- 可增, 可刪, 可替換
兩個值的時候, 就是刪除
arr = [1, 2, 3, 4, 5, 6]
// 從索引 2 的位置, 刪除 2個元素
arr.splice(2, 2)
console.log(arr)
[ 1, 2, 5, 6 ]
多個值的時候, 可以是新增, 比如在索引為 2 的位置, 往前新增兩個元素, 10, 11
arr = [1, 2, 3, 4, 5, 6]
// 索引為 2 的位置, 往前新增兩個元素, 10, 11
arr.splice(2, 0, 10, 11)
console.log(arr)
[ 1, 2, 10, 11, 3, 4, 5, 6 ]
同樣, 如果是要將索引為 2 的位置刪掉, 再替換也是一樣的, deleteCount
設定為 1 即可.
arr.splice(2, 1) // 表示刪掉索引為 2 的元素
arr.splice(2, 1, 10, 11) // 表示刪掉索引為 2 的元素, 並原地插入 10, 11
多維陣列
類似, 矩陣, 多層級的 json 等都基本可以透過一維陣列方式實現, 在實際應用中用得最多的就是二維陣列. 這裡我們以矩陣來做一個演示.
以兩天監測的平均華氏氣溫為例:
let avgTempDay01 = [ 72, 75, 79, 79, 81, 81]
let avgTempDay02 = [ 81, 79, 75, 75, 73, 72]
顯然這樣來儲存是比較分散的, 透過矩陣或者二維陣列的方式則更好一些.
let avgTempArr = []
// day01
avgTempArr[0] = []
avgTempArr[0][0] = 72
avgTempArr[0][1] = 75
avgTempArr[0][2] = 79
avgTempArr[0][3] = 79
avgTempArr[0][4] = 81
avgTempArr[0][5] = 81
// day02
avgTempArr[1] = []
avgTempArr[1][0] = 81
avgTempArr[1][1] = 79
avgTempArr[1][2] = 75
avgTempArr[1][3] = 75
avgTempArr[1][4] = 73
avgTempArr[1][5] = 72
console.log(avgTempArr);
PS F:\algorithms> node .\array.js
[ [ 72, 75, 79, 79, 81, 81 ], [ 81, 79, 75, 75, 73, 72 ] ]
當然也可以簡寫成這樣:
let avgTempArr = []
avgTempArr[0] = [ 72, 75, 79, 79, 81, 81]
avgTempArr[1] = [ 81, 79, 75, 75, 73, 72]
二維陣列迭代
- 巢狀 for 迴圈即可, 先行後列的方式
function printMatrix(matrix) {
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log('pos:', [i, j], matrix[i][j])
}
}
}
let avgTempArr = []
avgTempArr[0] = [ 72, 75, 79, 79, 81, 81]
avgTempArr[1] = [ 81, 79, 75, 75, 73, 72]
console.log(avgTempArr);
printMatrix(avgTempArr)
PS F:\algorithms> node .\array.js
[ [ 72, 75, 79, 79, 81, 81 ], [ 81, 79, 75, 75, 73, 72 ] ]
pos: [ 0, 0 ] 72
pos: [ 0, 1 ] 75
pos: [ 0, 2 ] 79
pos: [ 0, 3 ] 79
pos: [ 0, 4 ] 81
pos: [ 0, 5 ] 81
pos: [ 1, 0 ] 81
pos: [ 1, 1 ] 79
pos: [ 1, 2 ] 75
pos: [ 1, 3 ] 75
pos: [ 1, 4 ] 73
pos: [ 1, 5 ] 72
陣列常用方法
在JavaScript裡,陣列是經過改進的物件,這意味著建立的每個陣列都有一些可用的方法。陣列很有趣,因為它十分強大,和 Python 裡面的列表類似的. 且相比其他語言中的陣列,JavaScript中的陣列有許多很好用的方法.
常用陣列方法 | 描述 | 示例 |
---|---|---|
push / pop | 在尾部新增, 刪除元素 | arr.push(1, 2); item = arr.pop() |
unshift / shift | 在頭部新增, 刪除元素 | arr.unshift(); arr.shift() |
concat | 拼接2個或多個陣列, 並返回截個屏 | arr1.concat(arr2, arr3) |
join | 將陣列元素拼接為一個字串 | arr.join( ) 通常配合字元的 split 用 |
indexOf | 查詢元素第一次出現的索引, 沒有則 -1 | arr.indexOf('item') |
slice | 查詢索引範圍內的元素, 前閉後開 | arr.slice(1, 3) 返回索引為 1, 2 的元素 |
reverse | 將陣列元素反序, 原地修改 | arr.reverse() |
sort | 不傳參按字母排序, 傳參(函式) 按函式排 | arr.sort((a, b) => b - a) 降序 |
map | 傳回撥函式處理每個元素, 返回新陣列 | arr2 = arr.map((item) => item * 2) 每個元素平方 |
filter | 傳回撥呼聲判斷每個元素結果為 true 的, 返回新陣列 | arr2 = arr.filter((item) => item % 2 == 0) 只要偶數 |
forEach | 傳回撥函式, 遍歷陣列處理, 無返回值 | arr.forEach((item, index) => console.log(item, index)) |
every / some | 傳回撥函式, 判斷每個元素, 存在 / 所有 返回 Boolean | arr.some((item) => item % 2 == 0 ) |
includes | 判斷是否存在某個元素, 返回 Boolean | arr.includes('item') 感覺和 indexOf 也差不多 |
from / ... | 對陣列的淺複製 | arr2 = arr.from(arr) ; arr2 = [...arr] |
陣列/物件的深複製
陣列是引用型別, 在更多應用中會將一些資料包裝成陣列或物件形式傳給後端介面, 通常就淺複製就行, 如果不會在多個應用中共用的話:
- arr2 = [ ...arr ]
- arr2 = Array.from(arr)
- arr2 = arr.slice()
- arr2 = arr.map(item => item)
就淺複製不勝列舉. 但我們更多的是關注深複製.
第一種深複製的簡單粗暴方法是用 JSON.parse(JSON.stringify( ) )
即先將陣列序列化字串, 再進行反序列化成陣列.
const arr = [1, 2, 3, {'name': 'cj', age: 18}]
const arr2 = JSON.parse(JSON.stringify(arr))
arr[3].name = 'ccc'
console.log('arr: ', arr)
console.log('arr2: ', arr2)
PS F:\algorithms> node .\array.js
arr: [ 1, 2, 3, { name: 'ccc', age: 18 } ]
arr2: [ 1, 2, 3, { name: 'cj', age: 18 } ]
第二種方法是透過 遞迴
的方式層層遍歷陣列元素:
- 先定義一個空陣列, 或者空物件
- 遍歷陣列元素, 判斷當型別為 Array 時候, 再繼續遞迴遍歷
- 判斷當型別為 Object 時候, 再繼續遞迴遍歷
- 當為普通元素時, 新增進陣列 (遞迴終止條件)
function deepClone(obj) {
// 先要判斷 obj 是陣列, 物件, 還是基本型別
// 優先陣列處理, 因為陣列也是物件
if (Array.isArray(obj)) {
// 陣列
var ret = []
for (var item of obj) {
ret.push(deepClone(item))
}
} else if (typeof obj == 'object') {
// 物件
var ret = {}
for (var key in obj) {
ret[key] = deepClone(obj[key])
}
} else {
// 基本型別直接輸出, 作為遞迴出口
var ret = obj
}
return ret
}
const arr = [1, [{'a': 55}, 9], 3, {'name': 'cj', age: 18}]
const arr2 = deepClone(arr)
arr[3].name = 'ccc'
console.log('arr: ', arr)
console.log('arr2: ', arr2)
PS F:\algorithms> node .\array.js
arr: [ 1, [ { a: 55 }, 9 ], 3, { name: 'ccc', age: 18 } ]
arr2: [ 1, [ { a: 55 }, 9 ], 3, { name: 'cj', age: 18 } ]
陣列排序
先是反序的 reverse
原地方法.
const arr = [1, 2, 3, 'cj']
arr.reverse()
console.log(arr);
PS F:\algorithms> node .\array.js
[ 'cj', 3, 2, 1 ]
然後是陣列排序的 sort
方法. 當不傳函式時候, 就預設以字元排序, 注意不是數值哦, 一定要記得穿一個比較函式進去!!!
const arr = [1, 2, 80, 9]
arr.sort()
console.log(arr);
PS F:\algorithms> node .\array.js
[ 1, 2, 80, 9 ]
預設是字元排序, 所以進行升序或者降序的時候, 必須要傳入一個compareFn
.
const arr = [1, 2, 3, 1, 0, 'cj']
// a -b 升序預設, b-a 降序
arr.sort((a, b) => b -a )
console.log(arr);
PS F:\algorithms> node .\array.js
[ 3, 2, 1, 1, 0, 'cj' ]
至此, 關於 javascript 的陣列結構就差不多到這了.