JS 数组去重

总结

去重方法10w数据耗时ms复杂数据去重效果
双重循环3139普通类型除了 NaN,引用数据类型不能
Array.prototype.indexOf()2784普通类型除了 NaN,引用数据类型不能
Array.prototype.sort()53普通类型除了 NaN,引用数据类型不能
Array.prototype.includes()3018排序结果错误,相邻判断效果不好
Array.prototype.reduce()6普通数据类型都可以,引用数据类型不能
对象键值对(普通数据类型+特殊数据)60排序结果错误,相邻判断效果不好
对象键值对(复杂对象)77去重正确
Map9普通数据类型都可以,引用数据类型不能
set4普通数据类型都可以,引用数据类型不能

具体使用看情况而定

includes\Map\Set\Object的属性名是可以区分 NaN

测试数据

  • 10w 数据
const arr = [];
// 生成[0, 100000]之间的随机数
for (let i = 0; i < 100000; i++) {
  arr.push(0 + Math.floor((100000 - 0 + 1) * Math.random()))
}
  • 复杂数据
const arr = [1, 1, '1', '1', 0, 0, '0', '0', undefined, undefined, null, null, NaN, NaN, {}, {}, [], [], /a/, /a/];

双重循环

Array.prototype.unique = function () {
  const newArray = [];
  for (let i = 0; i < this.length; i++) {
    for (let j = i + 1; j < this.length; j++) {
      if (this[i] === this[j]) {
        j = ++i;
      }
    }
    newArray.push(this[i]);
  }
  return newArray;
}
  • 10w数组时间:3139.086181640625ms
  • 复杂情况结果:NaN Object Array 正则都无法区分,全等无法区分引用数据类型和 NaN

双重循环结果

Array.prototype.indexOf()

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

Array.prototype.unique = function () {
  const newArray = [];
  this.forEach(item => {
    if (newArray.indexOf(item) === -1) {
      newArray.push(item);
    }
  });
  return newArray;
}
  • 10w数组时间:2784.8837890625ms
  • 复杂情况结果:同上,全等无法区分引用数据类型和 NaN

indexOf

Array.prototype.sort()

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

Array.prototype.unique = function () {
  const newArray = [];
  this.sort();
  for (let i = 0; i < this.length; i++) {
    if (this[i] !== newArray[newArray.length - 1]) {
      newArray.push(this[i]);
    }
  }
  return newArray;
}
  • 10w数组时间:53.389892578125ms
  • 复杂数据结果:排序有问题,且全等无法判断引用数据类型和 NaN

sort

Array.prototype.includes()

语法:arr.includes(valueToFind[, fromIndex])

Array.prototype.unique = function () {
  const newArray = [];
  this.forEach(item => {
    if (!newArray.includes(item)) {
      newArray.push(item);
    }
  });
  return newArray;
}
  • 10w数据时间:3018.489013671875ms
  • 复杂数据效果:可以区分所有基础数据类型,引用类型不行。

includes

polyfill

f (!Array.prototype.includes) {
  Object.defineProperty(Array.prototype, 'includes', {
    value: function(valueToFind, fromIndex) {

      if (this == null) {
        throw new TypeError('"this" is null or not defined');
      }

      // 1. Let O be ? ToObject(this value).
      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // 3. If len is 0, return false.
      if (len === 0) {
        return false;
      }

      // 4. Let n be ? ToInteger(fromIndex).
      //    (If fromIndex is undefined, this step produces the value 0.)
      var n = fromIndex | 0;

      // 5. If n ≥ 0, then
      //  a. Let k be n.
      // 6. Else n < 0,
      //  a. Let k be len + n.
      //  b. If k < 0, let k be 0.
      var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

      function sameValueZero(x, y) {
        return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
      }

      // 7. Repeat, while k < len
      while (k < len) {
        // a. Let elementK be the result of ? Get(O, ! ToString(k)).
        // b. If SameValueZero(valueToFind, elementK) is true, return true.
        if (sameValueZero(o[k], valueToFind)) {
          return true;
        }
        // c. Increase k by 1.
        k++;
      }

      // 8. Return false
      return false;
    }
  });
}

Array.prototype.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;
  }, []);
}
  • 10w数据时间:6ms
  • 复杂数据效果:排序首先就错误了

reduce

对象键值对

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

需要注意:

  1. 无法区分隐式类型转换成字符串后一样的值,比如 1 和 ‘1’
  2. 无法处理复杂数据类型,比如对象(因为对象作为 key 会变成 [object Object])
  3. 特殊数据,比如 ‘proto’,因为对象的 proto 属性无法被重写

解决问题(1,3)

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;
}
  • 10w数据时间:60.39306640625ms
  • 复杂数据效果:效果较好,因为键值为类型+字符串,相同内容不同引用的对象转换成字符串是一样的。

object-key

tmp:

object-key

解决问题(2)

Array.prototype.unique = function () {
  const newArray = [];
  const tmp = {};
  for (let i = 0; i < this.length; i++) {
    // 使用JSON.stringify()进行序列化
    if (!tmp[typeof this[i] + JSON.stringify(this[i])]) {
      // 将对象序列化之后作为key来使用
      tmp[typeof this[i] + JSON.stringify(this[i])] = 1;
      newArray.push(this[i]);
    }
  }
  return newArray;
}

tmp:

object-key

  • 10w数据时间:77.2470703125ms
  • 复杂数据效果:对象序列化时,正则表达式结果为{},与空对象重复。

object-key

Map

键的比较是基于 SameValueZero 算法:NaN 和 NaN 相等,剩下其他的值是根据===运算符的结果判断是否相等。

Array.prototype.unique = function () {
  const tmp = new Map();
  return this.filter(item => {
    return !tmp.has(item) && tmp.set(item, 1);
  })
}
  • 10w数据时间:9.539794921875ms
  • 复杂数据效果:普通数据类型都可以,复杂数据类型不行

map

Set

set 中 NaN 之间被视为相同的值。

Array.prototype.unique = function () {
  return [...new Set(this)];
}
  • 10w数据时间:4.939208984375ms
  • 数据去重效果:普通数据类型可以,复杂数据类型不行

set

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值