javascript 数组去重的6种思绪

前端在一样平常开辟中或多或少都邑遇到有对数据去重的需求,实际上,像是lodash这些东西库已经有成熟完整的完成,而且能够成熟地运用于临盆环境。然则这并不阻碍我们从头脑拓展的角度动身,看看去重能够用几种思绪去完成。

首先是通例的双层轮回比对的思绪完成

function doubleLoopUniq(arr) {
  let result = [];
  for (let i = 0, len = arr.length, isExist; i < len; i++) {
    // 定义一个变量示意当前元素在 result 中是不是存在。
    isExist = false;
    for (let j = 0, rLen = result.length; j < rLen; j++) {
      if (result[j] === arr[i]) {
        // 顺次对result 中的元素 和 原数组元素举行比对。
        isExist = true;
        break;
      }
    }
    // 末了推断如果不存在,则将此元素插进去result
    !isExist && result.push(arr[i]);
  }
  return result;
}

借助 js内置的indexOf 举行去重

function indexOfUniq(arr) {
  let result = [];
  for (let i = 0, len = arr.length; i < len; i++) {
    // 用indexOf 简化了二层轮回的流程
    if (result.indexOf(arr[i]) === -1) result.push(arr[i]);
  }
  return result;
}

排序后前后比对去重

function sortUniq(arr) {
  let result = [], last;
  // 这里解构是为了不对原数组发生副作用
  [ ...arr ].sort().forEach(item => {
    if (item != last) {
      result.push(item);
      last = item;
    }
  });
  return result;
}

经由过程hashTable去重

function hashUniq(arr) {
  let hashTable = arr.reduce((result, curr, index, array) => {
    result[curr] = true;
    return result;
  }, {})
  return Object.keys(hashTable).map(item => parseInt(item, 10));
}

ES6 SET一行代码完成去重

function toSetUniq(arr) {
  return Array.from(new Set(arr));
}

splice 去重(直接操纵数组自身,带副作用)

function inPlaceUniq(arr) {
  let idx = 0;
  while (idx < arr.length) {
    let compare = idx + 1;
    while (compare < arr.length) {
      if (arr[idx] == arr[compare]) {
        arr.splice(compare, 1);
        continue;
      }
      ++compare
    }
    ++idx;
  }
  return arr;
}

末了在nodejs下面简朴跑个测试,看看哪一个效力高~

let data = [];
for (var i = 0; i < 100000; i++) {
  data.push(Math.random())
}

// 完成一个机能测试的装潢器
function performanceTest(fn, descript) {
  var a = new Date().getTime();
  return function () {
    fn.apply(this, [].slice.call(arguments, 0));
    console.log(descript, new Date().getTime() - a)
  }
}

performanceTest(hashUniq, "hashTable")(data)
performanceTest(sortUniq, "sortUniq")(data)
performanceTest(toSetUniq, "toSetUniq")(data)
performanceTest(indexOfUniq, "indexOfUniq")(data)
performanceTest(doubleLoopUniq, "doubleLoopUniq")(data)
performanceTest(inPlaceUniq, "inPlaceUniq")(data)

结果以下

hashTable 168ms
sortUniq 332ms
toSetUniq 80ms
indexOfUniq 4280ms
doubleLoopUniq 13303ms
inPlaceUniq 9977ms

延长思索: 如果数组内的元素是对象该怎样去重呢?

既然是援用范例,那末不免会使用到deepEqual,当然这类思绪能够解答这道题目,但不免不够高效。

从上面的测试中也可见经由过程new Set 和 hashTable 去重是最高效的。
所以毫无疑问,我们要基于这两种体式格局去革新,我想用的是hashTable,
另一方面,为了下降深度比较带来的耗时,我尝试用JSON.stringify 将援用范例转化为基础范例。

function collectionUniq(collection) {
  let hashTable = {};
  collection.forEach(item => {
    hashTable[JSON.stringify(item)] = true;
  })
  return Object.keys(hashTable).map(item => JSON.parse(item))
}

那末题目来了,我们都晓得对象的属性是无序的,如果数据是这类状况,那就GG了。

let collection = [ { a: 1, b: 2, c: 3 }, { b: 2, c: 3, a: 1 } ]

有一种toHash的思绪,在对这个数组举行一次基础的去重以后,为了保证正确,
先遍历JSON 字符串 =>
经由过程 charCodeAt()拿到每一个字符串 的 unicode 编码 =>
相加获得一个总数,末了再两两举行比较,数值相称的就是反复的,如许就到达去重的结果了。

function toHash(obj) {
  let power = 1;
  let res = 0;
  const string = JSON.stringify(obj, null, 2);
  for (let i = 0, l = string.length; i < l; i++) {
    switch (string[i]) {
      case '{':
        power *= 2
        break
      case '}':
        power /= 2
        break
      case ' ':
      case '\n':
      case '\r':
      case '\t':
      break
      default:
        res += string[i].charCodeAt(0) * power
    }
  }
  return res
}

这只是一个完成基础的思绪,有很大的革新空间,为了削减hash碰撞的能够,能够对一些特别字符举行权重的增减。

重点是保证碰撞的概率小到比中大奖还小就能够了。

2018.2.8
上面是一个比较清奇的思绪,通例的做法,实际上照样应当从优化深度比较的效力入手。
看到一个很好的完成思绪,是一个优先判错的思绪,经由过程预设种种前置条件来防止高价值的轮回,这类思绪只管在数据量小的时刻由于前置推断能够有一些微不足道的机能消耗,然则数据量越大,上风就越显著了。感兴趣的能够了解下。
https://github.com/epoberezki…

    原文作者:elliott_hu
    原文地址: https://segmentfault.com/a/1190000013192950
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞