先上题:
已知如下数组:
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
对于上述的编程题,相信很多童鞋都见过,也能分分钟写出答案。该题有三个考察点:
- 数组的扁平化
- 数组的去重
- 数组的排序
可以说这都是很基本的操作了,如果你熟悉ES6,那么你可能更加喜欢用Array.flat来扁平,使用Set来去重,使用sort来排序。
也就是一行代码的事
Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>{ return a-b})
我相信这也可能是很多面试官给出的标准答案。
而这个就是最优答案吗?
由于笔者最近在做性能优化,需要在弱性能机器上跑比较复杂的程序,所以对每一行代码的执行时间都特别在意。
所以我们对比下:Set+Flat和原生数组实现的性能消耗
以下数据是在Chrome80下执行所得
去重对比:Set pk 数组遍历+includes
function uniqueBySet(arr) {
return Array.from(new Set(arr));
}
function uniqueByIncludes(arr) {
const res = [];
for (let i = 0; i < arr.length; i++) {
const a = arr[i];
!res.includes(a) && res.push(a)
}
return res;
}
扁平化对比:Array.prototype.Flat pk 递归遍历
function flat(arr) {
return arr.flat(Infinity);
}
function flatByArray(arr, res) {
res = res || [];
for (let i = 0; i < arr.length; i++) {
const a = arr[i];
if (isArray(a)) {
flatByArray(a, res)
} else {
res.push(a)
}
}
return res
}
测试代码:
通过console.time查看代码执行时间
function isArray(item) {
return Object.prototype.toString.call(item) === '[object Array]';
}
function set_flat(arr) {
console.time('flat');
let _arr = flat(arr);
console.timeEnd('flat');
console.time('uniqueBySet');
_arr = uniqueBySet(_arr);
console.timeEnd('uniqueBySet');
return _arr.sort((a,b)=>{ return a-b})
}
function array_includes(arr) {
console.time('flatByArray');
let _arr = flatByArray(arr);
console.timeEnd('flatByArray');
console.time('uniqueByIncludes');
_arr = uniqueByIncludes(_arr);
console.timeEnd('uniqueByIncludes');
return _arr.sort((a,b)=>{ return a-b})
}
// 产生长度为300000的数组用于测试,通过console.time查看代码执行时间
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
var array = []
for (let j = 0; j < 300000; j++) {
array.push(arr)
}
[set_flat, array_includes].forEach(fun => {
console.time(fun.name)
console.log(fun(array));
console.timeEnd(fun.name)
})
执行结果
对于这个结果,笔者是有点意外。
原生提供的Array.prototype.flat比不上使用数组递归遍历实现。
不过Set去重性能还是比原生的遍历好。
由于扁平化性能消耗相差太大,也导致上述那个非常优雅简洁的答案的性能消耗是原生实现的5倍
总结
从上面的对比可以看出以下答案并非是最优的,至少在性能消耗这一块
Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>{ return a-b})
如果从代码整洁度出发,数据量不大,机器性能还可以的情况下,可以使用上面的写法。
但如果数据量很大,而机器性能又不是很好的时候,flat扁平化处理还是使用原生的递归遍历实现为好。
所以去重可以使用Set。但是扁平化,是否使用Array.prototype.flat,需要根据实际情况考虑
有什么不对的地方,欢迎指正和交流!