数据结构-数组

数组是一种最为常见和应用广泛的数据结构, 不用语言的数组不太一样, 对于 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 的数组结构就差不多到这了.

posted @ 2024-03-26 19:22  致于数据科学家的小陈  阅读(2)  评论(0编辑  收藏  举报