常见的数组技巧

前端开发的过程中,常常需要我们操作数组来获取想要的数据,今天就总结一些在 JS 开发中我们需要常用到的方法。

去重

数组去重的方法大家都知道几个,我现在想从优化耗时的角度去思考。

创建测试模板

使用 timetimeEnd 来检测测试函数运行时间,就可以了解各个方法的性能。

  1. // 创建两个比较大的数据的数组
  2. let arr1 = Array.from(new Array(100000), (x, index) => index);
  3. let arr2 = Array.from(new Array(50000), (x, index) => index + index);
  4. // 定义一个测试方法
  5. const test = function() {};
  6. // 计时
  7. console.time();
  8. // 执行函数
  9. test();
  10. // 结束
  11. console.timeEnd();

开始

使用双重for循环

解释:外层遍历每一个元素,内循环比较一遍,有相同的使用push或者splice

  1. function test(a, b) {
  2. const arr = a.concat(b);
  3. for (let i = 0; i < arr.length; i++) {
  4. for (let j = i + 1; j < arr.length; j++) {
  5. if (arr[i] === arr[j]) {
  6. arr.splice(j, 1);
  7. // splice 改变数组长度
  8. arr.length--;
  9. j--;
  10. }
  11. }
  12. }
  13. }

default: 11152.2509765625ms

使用filterindexOf

解释:这个实现比较简单,利用filter返回条件为真的数组元素,indexOf会返回检索的字符在整个数组中的位置,正好和数组的下标对应。

  1. function test(a, b) {
  2. const arr = a.concat(b);
  3. return arr.filter((item, index) => {
  4. return arr.indexOf(item) === index;
  5. });
  6. }

default: 10116.416015625ms

使用indexOffor循环

解释: 返回数组中某个指定的元素的位置,没有返回则返回-1.

  1. function test(a, b) {
  2. const arr = a.concat(b);
  3. for (let i = 0; i < arr.length; i++) {
  4. if (arr.index(arr[i]) === -1) {
  5. arr.push(arr[i]);
  6. }
  7. }
  8. return arr;
  9. }

default: 9876.494873046875ms

使用数组方法includes

解释:传入指定元素,如果存在返回true,没有则返回false

  1. function test(a, b) {
  2. const arr = a.concat(b);
  3. let arr1 = [];
  4. for (let i = 0; i < arr.length; i++) {
  5. if (!arr1.includes(arr[i])) {
  6. arr1.push(arr[i]);
  7. }
  8. }
  9. return arr1;
  10. }

default: 9848.931884765625ms

使用sort方法

解释:使用相邻元素比较的方法

  1. function test(a, b) {
  2. let arr = a.concat(b);
  3. arr = arr.sort();
  4. const arr1 = [arr[0]];
  5. for (let i = 0; i < arr.length; i++) {
  6. if (arr[i] !== arr[i - 1]) {
  7. arr1.push(arr[i]);
  8. }
  9. }
  10. return arr1;
  11. }

default: 26.5078125ms

使用 Map

解释:创建一个空 Map 的数据结构,遍历需要去重的数组,把数组的每一个元素作为 key 存入 map 中。由于 map 不会出现相同的 key 实现去重,最后得到去重的结果。

  1. function test(a, b) {
  2. let arr = a.concat(b);
  3. let map = new Map();
  4. let arr1 = [];
  5. for (let i = 0; i < arr.length; i++) {
  6. if (map.has(arr[i])) {
  7. map.set(arr[i], true);
  8. } else {
  9. map.set(arr[i], false);
  10. arr1.push(arr[i]);
  11. }
  12. }
  13. return arr1;
  14. }

default: 28.06005859375ms

使用 Set 和展开运算符…

解释:SetES6 新增的一个对象,允许存储任何类型(原始值或引用值)的唯一值.

  1. function test(a, b) {
  2. let arr = a.concat(b);
  3. // 一
  4. const arr1 = Array.from(new Set(arr));
  5. // 二
  6. // const arr1 = [...new Set(arr)]
  7. console.log(arr1);
  8. }

default: 18.821044921875ms

使用 for...ofObject 的属性不会重复的特点

解释: ES6 中引入的 for…of 循环,以替代 for…in 和 forEach() ,并支持新的迭代协议。for…of 允许你遍历 Arrays(数组), Strings(字符串), Maps(映射), Sets(集合)等可迭代的数据结构等。

  1. function test(a, b) {
  2. const arr = a.concat(b);
  3. const arr1 = [];
  4. const obj = {};
  5. for (let i of arr) {
  6. if (!obj[i]) {
  7. arr1.push(i);
  8. obj[i] = 1;
  9. }
  10. }
  11. return arr1;
  12. }

default: 19.759033203125ms

小节

其实去重的方法还有很多,毕竟个人经历有限就不全部列举出来了,在这里我们就能够看出来,谁的执行时间比较短,总结来说还是使用 Set 最为方便。

splice 实现增删改

array.splice(start[,deleteCount[,item1[,item2[,…]]]]) 详情请参照

start
指定修改的开始位置(从0计数)。如果超出了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n 是倒数第 n 个元素并且等价于array.length-n);如果负数的绝对值大于数组的长度,则表示开始位置为第0位。

deleteCount 可选
整数,表示要移除的数组元素的个数。

如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。
如果 deleteCount 被省略了,或者它的值大于等于 array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。
如果 deleteCount0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。

item1, item2, ... 可选
要添加进数组的元素,从 start 位置开始。如果不指定,则 splice() 将只删除数组元素。

splice 的删除

  1. const arr = [1, 2, 3, 4];
  2. /* start为正数 */
  3. // 1.如果不传递要移除的数组元素的参数, 默认删除开始位置(包括)后面所有的元素
  4. const removed = arr.splice(1);
  5. console.log(arr); // [2]
  6. console.log(removed); // [1, 3, 4]
  7. // 2.传递参数,表示要删除的个数
  8. const removed = arr.splice(1, 1);
  9. console.log(arr); // [1, 3, 4]
  10. console.log(removed); // [2]
  11. /* start为负数,数组将从后往前 */
  12. // 1.如果不传递要移除的元素的参数,且传入的start为负数,则负号表示的含义就是:从倒数第几个参数开始,后面的全部删除
  13. const removed = arr.splice(-2);
  14. console.log(arr); // [1, 2]
  15. console.log(removed); // [3,4]

splice 的添加

  1. // 添加其实很好理解,就是删除0个元素
  2. const arr = [1, 2, 3, 4];
  3. // 在数组下标为1的位置删除0个元素,并插入一个新的元素 5
  4. const removed = arr.splice(1, 0, 5);
  5. console.log(arr); // [1,5,2,3,4]
  6. console.log(removed); // []

splice 的修改

  1. // 修改就是删除元素的同时再添加新的元素
  2. const arr = [1, 2, 3, 4];
  3. // 在数组下标为1 的地方删除1个元素,同时插入一个元素
  4. const removed = arr.splice(1, 1, [5]);
  5. console.log(arr); // [1, [5], 3, 4]
  6. console.log(removed); // [2]

splice 的进阶

我们可以通过上面的方法进行一个简单的总结:

  1. 我们可以使用 splice 模拟数组的其他方法

    push: splice(arr.length, 0, x)
    unshift: splice(0, 0, x)
    shift: splice(0, 1)
    pop: splice(arr.length-1, 1)

生成随机数

生成 x 到 y 之间的随机数

  1. function random(x, y) {
  2. // 这样不需要判断x和y的大小
  3. return Math.floor(y - Math.random() * (y - x));
  4. }

拓展:生成随机颜色

  1. function random(x) {
  2. return Math.floor(Math.random() * x);
  3. }
  4. function rgb() {
  5. //rgb颜色随机
  6. const r = random(256);
  7. const g = random(256);
  8. const b = random(256);
  9. const rgb = "(" + r + "," + g + "," + b + ")";
  10. // 转换成16进制
  11. // const color = '#' + r.toString(16) + g.toString(16) + b.toString(16)
  12. return rgb;
  13. }

filter

方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。

  1. 去除假值
  1. function boo(item) {
  2. if (item) {
  3. return true;
  4. }
  5. }
  6. const arr = ["", 0, false, NaN, undefined, true, {}];
  7. const filterArr = arr.filter(boo);
  8. // 等价于下面这种方法
  9. // const filterArr = arr.filter(Boolean)
  1. 配合 indexOf 或者 include 进行去重
  1. const arr = [1, 1, 1, 2, 3, 4, 2, 4];
  2. const filterArr = arr.filter((item, index, arr) => arr.indexOf(item) === index);

当然我在上面也说了这个方法在进行较大数据的时候,性能很低,请酌情使用。

  1. 求数组中的交集
  1. const arr1 = [1, 2, 3, 4, 5];
  2. const arr2 = [2, 3, 4];
  3. const arr = arr2.filter(item => arr1.indexOf(item) !== -1);
  4. // const arr = arr1.filter(item => arr2.includes(item))

reduce

这个方法很重要也很实用,其实列举起来有很多的高级功能和方法。
本来也打算做一个比较好的总结,但是大家可以看下面的会更好!
详情请参照 25 个你不得不知道的数组 reduce 高级用法(必备硬核骚技能)