这应该是JavaScript 中数组操作方法(含ES5及ES5+)较全的总结了吧

一、ES5 中数组操作方法

ES5 中数组操作方法总结

详尽使用参看 MDN_Array

二、ES5+ 中数组操作方法

ES5+ 数组操作方法

除了 flat 和 flatMap 方法详解,其它详解参看 ES6 数组给我们带来哪些操作便利?

flat()和flatMap()本质上就是是归纳(reduce) 与 合并(concat)的操作。

Array.prototype.flat()

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

  • flat()方法最基本的作用就是数组降维
var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

//使用 Infinity 作为深度,展开任意深度的嵌套数组
arr3.flat(Infinity); 
// [1, 2, 3, 4, 5, 6]
  • 其次,还可以利用 flat() 方法的特性来去除数组的空项
var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]

具体可参看 MDN_Array.prototype.flat()

Array.prototype.flatMap()

flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。 这里我们拿map方法与flatMap方法做一个比较。

var arr1 = [1, 2, 3, 4];

arr1.map(x => [x * 2]); 
// [[2], [4], [6], [8]]

arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]

// 只会将 flatMap 中的函数返回的数组 “压平” 一层
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]

具体参看 MDN_Array.prototype.flatMap()

三、数组操作方法对数组自身的影响

是否改变数组自身

四、九种数组去重方法

9重数组去重方法

测试数组去重时间代码示例

const arr = [];
// 生成[0, 100000]之间的随机数
for (let i = 0; i < 100000; i++) {
  arr.push(0 + Math.floor((100000 - 0 + 1) * Math.random()))
}

// ...实现算法

console.time('test');
arr.unique();
console.timeEnd('test');
1、双重去重

基本思路:通过拿出一个元素和剩下的元素依次比较,如果全部不相等则证明此元素为唯一。

Array.prototype.unique = function () {
  const newArray = [];
  let isRepeat;
  for (let i = 0; i < this.length; i++) {
    isRepeat = false;
    for (let j = 0; j < newArray.length; j++) {
      if (this[i] === newArray[j]) {
        isRepeat = true;
        break;
      }
    }
    if (!isRepeat) {
      newArray.push(this[i]);
    }
  }
  return newArray;
}

测试时间:

test: 3032.134033203125ms

缺点:复杂度太高

2、indexOf

基本思路:如果索引不是第一个索引,说明是重复值。

Array.prototype.unique = function () {
  const newArray = [];
  this.forEach(item => {
    if (newArray.indexOf(item) === -1) {
      newArray.push(item);
    }
  });
  return newArray;
}

测试时间:

test: 6951.115966796875ms
3、sort

基本思路:先对原数组进行排序,然后再进行元素比较。

Array.prototype.unique = function () {
  const newArray = [];
  this.sort();
  for (let i = 0; i < this.length; i++) {
    if (this[i] !== this[i + 1]) {
      newArray.push(this[i]);
    }
  }
  return newArray;
}

测试时间:

test: 72.129150390625ms
4、includes

基本思路:判断新数组中是否含有已存在的项

Array.prototype.unique = function () {
  const newArray = [];
  this.forEach(item => {
    if (!newArray.includes(item)) {
      newArray.push(item);
    }
  });
  return newArray;
}

测试时间:

test: 5378.988037109375ms
5、reduce

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值

Array.prototype.unique = function () {
  return this.sort().reduce((init, current) => {
    if(init.length === 0 || init[init.length - 1] !== current){
      init.push(current);
    }
    return init;
  }, []);
}

测试时间:

test: 69.05908203125ms

更好的使用场景:对象数组中去掉重复的对象

6、对象键值对keys()

基本思路:利用了对象的key不可以重复的特性来进行去重。

Array.prototype.unique = function () {
  const newArray = [];
  const tmp = {};
  for (let i = 0; i < this.length; i++) {
    if (!tmp[typeof this[i] + this[i]]) {
      tmp[typeof this[i] + this[i]] = 1;
      newArray.push(this[i]);
    }
  }
  return newArray;
}

测试时间:

test: 333.0009765625ms

更适合处理如对象这样的复杂的数据类型

7、Map 数据结构

两种针对简单数据类型的去重:
方法一:

Array.prototype.unique = function () {
  const newArray = [];
  const tmp = new Map();
  for(let i = 0; i < this.length; i++){
        if(!tmp.get(this[i])){
            tmp.set(this[i], 1);
            newArray.push(this[i]);
        }
    }
    return newArray;
}

方法二:

Array.prototype.unique = function () {
  const tmp = new Map();
  return this.filter(item => {
    return !tmp.has(item) && tmp.set(item, 1);
  })
}

分别的测试时间:

test: 53.268798828125ms
test: 41.73193359375ms
8、Set 数据结构

也有两种简单的数据类型去重的:
方法一:

Array.prototype.unique = function () {
  const set = new Set(this);
  return Array.from(set);
}

方法二:

Array.prototype.unique = function () {
  return [...new Set(this)];
}

测试时间:

test: 17.760009765625ms
test: 19.72509765625ms

上面两种方法只适用简单的数据类型去重,如果遇到一个比较复杂的对象数组,就去重失败,如下代码示例:

let arr = [
    { name: 'a', num: 1},
    { name: 'b', num: 1},
    { name: 'c', num: 1},
    { name: 'd', num: 1},
    { name: 'a', num: 1},
    { name: 'a', num: 1},
    { name: 'a', num: 1}
]
function unique (arr) {
    return [...new Set(arr)]
}
console.log(unique(arr)) // 结果为原数组,有兴趣可以复制代码试一下

因为 set 数据结构认为对象永不相等,即使是两个空对象,在 set 结构内部也是不等的
如何解决呢?
这里需要借助 JSON.stringfy() 和 JSON.parse() 序列化对象:

// ES6对象数组所有属性去重,筛选每个数组项的字符
function unique(arr) {
    const map = new Map()
    return arr.filter( item => !map.has(JSON.stringify(item)) && map.set(JSON.stringify(item), 1))
}

function unique(arr) {
      return [...new Set(arr.map(e => JSON.stringify(e)))].map(e => JSON.parse(e))
  }

// 两者出来的结果
[
    { name: 'a', num: 1},
    { name: 'b', num: 1},
    { name: 'c', num: 1},
    { name: 'd', num: 1}
]
9、underscore.js 方法库

测试代码

_.uniq(arr);

测试时间

test: 3640.329833984375ms

总结
除了考虑时间复杂度外、性能之外,还要考虑数组元素的数据类型等问题权衡选择出采用哪种算法,通过上面的几个测试用例,可以看出利用 Set 和 Map 数据结构去重的效率会高一些。

你可能感兴趣的:(这应该是JavaScript 中数组操作方法(含ES5及ES5+)较全的总结了吧)