前端在平常开发中或多或少都会碰到有对数据去重的需求,实际上,像是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 进行去重node
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; }
排序后先后比对去重git
function sortUniq(arr) { let result = [], last; // 这里解构是为了避免对原数组产生反作用 [ ...arr ].sort().forEach(item => { if (item != last) { result.push(item); last = item; } }); return result; }
经过hashTable去重github
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 去重(直接操做数组自己,带反作用)app
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下面简单跑个测试,看看哪一个效率高~dom
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
延伸思考: 若是数组内的元素是对象该怎么去重呢?oop
既然是引用类型,那么难免会使用到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...