数组总结(main), js数组

305 阅读5分钟

1. 是否改变原数组的常用方法

1. 改变原数组的:

shift:将第一个元素删除并且返回删除元素,空即为undefined
unshift:向数组开头添加元素,并返回新的长度
pop:删除最后一个并返回删除的元素
push:向数组末尾添加元素,并返回新的长度
reverse:颠倒数组顺序
sort:对数组排序
splice:splice(start,length,item)删,增,替换数组元素,返回被删除数 组,无删除则不返回

2. 不改变原数组的:

concat:连接多个数组,返回新的数组
join:将数组中所有元素以参数作为分隔符放入一个字符
slice:slice(start,end),返回选定元素 map,filter,forEach,some,every等不改变原数组

2. 数组去重

let arr = [
  1,
  undefined,
  2,
  11,
  NaN,
  NaN,
  "a",
  1,
  2,
  3321,
  4,
  null,
  4,
  4,
  null,
  2,
  "dd",
  "a",
  "d",
  undefined
];
// # 方法1: es6的 new Set()方法 ==> (推荐)
Array.from(new Set(arr));
// 方法2: push方法。新建一新数组,遍历数组每一项,值若不在新数组就push进该新数组中
// 这种方法 NaN 无法去重
let newArr = [];
for (let i = 0; i < arr.length; i++) {
  const ele = arr[i];
  console.log(arr[i]);
  if (newArr.indexOf(arr[i]) === -1) {
    newArr.push(arr[i]);
  }
}
console.log(newArr);
// 方法3: splice方法。 双重for循环, 若第二层循环跟第一层循环的值, 就通过arr.splice(j, 1)删除, 不要忘记j--
// NaN无法去重
for (let i = 0; i < arr.length; i++) {
  for (let j = i + 1; j < arr.length; j++) {
    if (arr[i] === arr[j]) {
      arr.splice(j, 1);
      j--;
    }
  }
}

// 方法4: 排序后相邻去除法, 会打乱原来数组的排序。 可以用split删除, 也可以用只push不与前一值重复的值两种方法
let sortArr = arr.sort();
console.log(sortArr);
console.log(sortArr.length);
let temp = [sortArr[0]];
for (let i = 0; i < sortArr.length; i++) {
  // 方法1: splice方法 不能去重NaN
  if (i < sortArr.length - 1 && sortArr[i] === sortArr[i + 1]) {
    sortArr.splice(i + 1, 1);
    i--;
  }
  // 方法2: push方法 如果sortArr[i]不与temp的最后一位相等, 就push进temp中。 不能去重NaN
  if (sortArr[i] !== temp[temp.length - 1]) {
    temp.push(sortArr[i]);
  }
}
console.log(temp);
console.log(sortArr);
console.log(arr);

3. 数组浅拷贝和深拷贝

概念

浅拷贝:只拷贝一层,深层次的对象级别只拷贝引用。 浅拷贝就是流于表面的拷贝方式;当属性值为对象类型时,只拷贝了对象数据的引用,导致新旧数据没有完全分离,还会互相影响

深拷贝:拷贝多层,每一级别的数据都会被拷贝出来。 拷贝之后新旧数据完全分离,不再共用对象类型的属性值,不会互相影响

1.浅拷贝的实现方式

// 方法1 通用循环
function shallowCopy(obj) {
  if (typeof obj !== 'object') return;
  //判断目标类型,来创建返回值
  const newObj = obj instanceof Array ? [] : {};
  for(let key in obj) {
    //只复制元素自身的属性,不复制原型链上的
    if (obj.hasOwnProperty(key)) {
      newObj[key] = obj[key];
    }
  }

  return newObj;
}

// 方法2 Object.assign
const newObj = Object.assign({}, oldObj);

// 方法3:Array.slice
const newArray = oldArray.slice(); 

// 方法4: Array.concat
const newArray = oldArray.concat();

//# 方法5: es6 (推荐)
const arr = [1, 2, 3]
const arrClone = [...arr]
// 对象也可以这样浅拷贝
const obj = { a: 1 }
const objClone = { ...obj }

2. 深拷贝的实现方式

const obj = {
  info: {
    name: 'kobe',
    nums: [1,2,3]
  },
  persons: [person1, person2]
}
// 方法一:通用循环
function deepCopy(obj) {
  if (typeof obj !== 'object') return;

  const newObj = obj instanceof Array ? [] : {};

  for(let key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = typeof obj === 'object' ? deepCopy(obj[key]) : obj[key];
    }
  }

  return newObj;
}

// 方法二:JSON.stringify,JSON.parse   不能深拷贝属性值是函数的对象
// 在JSON.stringify的时候很多规则会使最后JSON.parse出来的对象不太一样。
const newObj = JSON.parse(JSON.stringify(oldObj));

总结: 一定要理解造成浅拷贝的原因:对象类型数据复制时,复制了引用地址,用的还是同一个数据对象;所以实现深拷贝的方式就是要对 对象类型属性值递归进行深拷贝,避免直接赋值。

4. 数组转对象的方法

1. es6新方法之 Object.assign();

let arr = [
    { user: "123" },
    { Cause: "22" },
    { EnterFactoryTime: "33" }
];
let res = Object.assign(...arr)
/* 
    arr = {
        user: "123",
        Cause: "22",
        EnterFactiryTime: "33"
    }
*/

let arr2 = ['a', 'c', 'ddd'];
let res2 = Object.assign({}, ['a', 'c', 'cfg'])
/* 
    arr2 = {
        0: "a",
        1: "d",
        2: "d"
    }
*/

5. 多维数组转一维数组的方法

let arr = [[1, [2, 9]], [3, 4, undefined, null], [5, 6, '', ' ']];
// 方法1: 利用arr.reduce(callback [, initialValue])
let arr1 = arr.reduce((a, b) => a.concat(b))
// 方法2: 利用apply
let arr2 = [].concat.apply([], arr);
// 方法3: 利用es6, 优点是 多维数组也可以
function flatten(arr) { return [].concat(...arr.map(x => Array.isArray(x) ? flatten(x) : x)) }
let arr3 = flatten(arr);
// 方法4: 通过将数组转变成字符串,利用str.split(','), 然后用map(Number)将字符串数组转整形实现。
//优点是多维数组也可以,缺点是数组元素都变字符串了。 会将数组中的空值全转为0。
let arr4 = (arr + '').split(",").map(Number);
// let arr4 = arr.toString().split(",").map(Number);
/* # 推荐 方法5: ES最新语法 Array.prototype.flat()。优点非常简单
 语法:var newArray = arr.flat(depth),参数说明:depth,可选,指定嵌套数组中的结构深度,默认值为1。
 可以写1,2,..., Infinity
 Infinity展开所有嵌套数组  */
let arr5 = arr.flat()
let arr6 = arr.flat(2)
// 方法6 利用正则
let res7 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '') + ']');

6. 判断是否为数组的方法

一共包含5种方法:

  1. arr instanceof Array

  2. Array.isArray(arr)

  3. Object.prototype.toString.call(arr) === "[object Array]" 或者 {}.toString.call(arr) === "[object Array]"

  4. Array.prototype.isPrototypeOf(arr)

  5. arr.constructor.toString().indexOf("Array") !== -1

// 一共包含5种方法
let arr = [1, 2, 3];
// 方法1:  instanceof 返回布尔值
console.log(arr instanceof Array); // true
// 方法2: 原型prototype + toString + call();
console.log(Object.prototype.toString.call(arr)); //"[Object Array]"
console.log(Object.prototype.toString.call(arr).indexOf("Array") !== -1); // true
/*   方法3: 原型prototype + isPrototypeOf() 
isPrototypeOf() 函数 : 用于指示对象是否存在于一个对象的原型链中。如果存在返回true,反之返回false。该方法属Object对象,由于所有的对象都继承了Object的对象实例,因此几乎所有的实例对象都可以使用该方法。如果variable的原型链中存在Array对象,就会返回true,也就说明variable是数组类型。 */
console.log(Array.prototype.isPrototypeOf(arr)); //true
// 方法4: 构造函数 constructor 注意下面的toString方法是带圆括号的
console.log(arr.constructor.toString()); // function Array() { [native code] }
console.log(arr.constructor.toString().indexOf("Array") !== -1); // true
// 方法5: 数组方法 isArray()  ==>(推荐)
console.log(Array.isArray(arr)); //true

7. 判断数组是空数组[]的方法

方法1: 判断数组长度是否为0。
方法2: 使用JSON.stringify直接判断是否等于空数组的字符串 提示: 最好能先判断下是否是数组, 再用&&判断数组是空数组

let arr = [];
arr.length === 0;  // true
JSON.stringify(arr) === "[]";  //true

8. 类数组转变成数组的6种方法

把符合以下条件的对象称为类数组
1、具有length属性
2、按索引方式存储数据
3、不具有数组的push、pop等方法

  // 自定义一个伪数组
  let likeArr = { 0: "伪", 1: "类", 2: "数", 3: "组", length: 4 };
  console.log(Object.prototype.toString.call(likeArr) === '[object Object]'); // true
  let arr = [];
  console.log(Object.prototype.toString.call(arr) === "[object Array]"); // true
  // 方法1: for循环赋值
  for (let i = 0; i < likeArr.length; i++) {
    arr[i] = likeArr[i];
  }
  // 方法2: for循环把伪数组每一项push到数组中
  for (let i = 0; i < likeArr.length; i++) {
    arr.push(likeArr[i]);
  }
  // 方法3: 空数组的call方法 (建议使用此方法)
  arr = [].slice.call(likeArr);
  // 方法4: 数组原型的call方法
  arr = Array.prototype.slice.call(likeArr);
  // 方法5: Array.from方法
  arr = Array.from(likeArr);
  // 方法6: 原型 __protp__
  likeArr.__proto__ = Array.prototype;

9. 获取数组中的最大值和最小值

// 不能包含undefined和字符串  否则会转成NaN。 数组中的null , '' 会转换成0
let arr = [2, 5, 1, 11, 4214, "", null, " ", -1];
console.log("max:", Math.max.apply(this, arr)); // 4214
console.log("max2:", Math.max.call(this, ...arr)); // 4214
console.log("min:", Math.min.apply(this, arr)); // -1
console.log("min:", Math.min.call(this, ...arr)); // -1

10. 生成大量测试数据,类似[1,100]

测试大量数据的数组时可以这样生成:

// fill
const arr = new Array(100).fill(0).map((item, index) => index + 1)

// Array.from()
const arr = Array.from(Array(100), (v, k) => k + 1)

// ... + array.keys() 生成的是0-99的数组
const ary = [...Array(100).keys()] 

new Array(100) 会生成一个有100空位的数组,这个数组是不能被map()forEach(), filter(), reduce(), every()some()遍历的,因为空位会被跳过(for of不会跳过空位,可以遍历)。 [...new Array(4)] 可以给空位设置默认值undefined,从而使数组可以被以上方法遍历。

11. 数组取交集、差集、并集

const a = [0, 1, 2, 3, 4, 5]
const b = [3, 4, 5, 6, 7, 8]
// # 交集
1. const intersection = a.filter(v => b.includes(v))  // [3, 4, 5] 
2. const duplicatedValues = [...new Set(a)].filter(item => b.includes(item))  // [3, 4, 5]
// # 差集
1. let difference = a.concat(b).filter(v => !a.includes(v) || !b.includes(v)) // [0, 1, 2, 6, 7, 8]
2. const diffValues = [...new Set([...a, ...b])].filter(item => !b.includes(item) || !a.includes(item)) // [0, 1, 2, 6, 7, 8]
// # 并集
const union = a.concat(b.filter(v => !a.includes(v)))  // [0, 1, 2, 3, 4, 5, 6, 7, 8]