JavaScript数组去重方案演进:从对象键值到深度递归比较

作者:小括号

时间:2026-10-27

浏览:592

本文深入探讨JavaScript中数组去重的多种实现路径。首先分析利用对象Key不重复特性的基础方法及其在类型转换中的缺陷;随后通过类型前缀改进方案,最后引入基于递归的深度比较算法,以解决复杂对象和函数的去重难题,帮助开发者根据实际性能需求选择合适方案。

文章目录

基于对象Key的唯一性去重方案

1. 基于JavaScript对象中Key唯一性的特性,可以构思出一种数组去重的思路。按照最常规的逻辑,实现方式如下:

代码如下:

function distinctArray(arr){
var obj={},temp=[];
for(var i=0;i<arr.length;i++){
if(!obj[arr[i]]){
temp.push(arr[i]);
obj[arr[i]] =true;
}
}
return temp;
   }
var testarr=[1,2,3,2];
console.log(distinctArray(testarr));// [1,2,3]

这种方法看似效果不错,但如果遇到以下情况:


var testarr1=[1,2,3,"2"];


console.log(distinctArray(testarr));// [1,2,3]


结果依然相同,这显然不符合预期。我们期望的结果应当是[1,2,3,"2"],即在去重过程中必须严格保留数据类型的一致性。

针对上述问题,我们对前一种方法进行改进:

代码如下:

function distinctArrayImprove(arr){
var obj={},temp=[];
for(var i=0;i<arr.length;i++){
if(!obj[typeof (arr[i])+arr[i]]){
temp.push(arr[i]);
obj[typeof (arr[i])+arr[i]] =true;
}
}
return temp;
}

在上述方法中,我们在构建对象Key时加入了typeof前缀,从而区分不同数据类型。让我们看看改进后的效果:


var testarr1=[1,2,3,"2"];


console.log(distinctArray(testarr));// [1,2,3,"2"]


效果不错!但这是否意味着该函数已经完美无缺?让我们继续观察另一种情况:


var testarr1=[1,2,3,"2",{a:1},{b:1}];


console.log(distinctArray(testarr));// [1,2,3,"2",{a:1}]


结果出现了异常,{b:1}被意外删除。在去重过程中误删有效数据是严重问题,因此上述方法并非完美方案,让我们继续往下看。

基于slice和splice的迭代去重方案

2. 在第一种方法中,核心思想是利用JS对象Key不重复的特性,但未能解决所有问题。我们可以尝试换一种思路,利用slice和splice方法来实现数组去重,如下:

代码如下:

function distinctArray2(arr){
var temp=arr.slice(0);//数组复制一份到temp
for(var i=0;i<temp.length;i++){
for(j=i+1;j<temp.length;j++){
if(temp[j]==temp[i]){
temp.splice(j,1);//删除该元素
j--;
}
}
}
return temp;
}

测试:


var testarr1=[1,2,3,"2"];


console.log(distinctArray(testarr));// [1,2,3]


var testarr2=[1,2,2,{a:1},{a:1},{a:1,b:2},function(){alert("b");},function(){alert("b");}];


//[1,2,{a:1},{a:1},{a:1,b:2},function(){alert("b");},function(){alert("b");}]

测试结果仍不满足需求。经过研究,主要问题在于比较两个对象是否相等。distinctArray2中使用“==”进行比较,无法准确判断大对象内容是否相等。为此,我们编写了一个新的方法:

基于深度递归比较的通用去重方案

代码如下:

function distinctArrayAll(arr){
var isEqual=function(obj1,obj2){
//两个对象地址相等,必相等
if(obj1===obj2){
return true;
}
if(typeof(obj1)==typeof(obj2)){
if(typeof(obj1)=="object"&&typeof(obj2)=="object"){
var pcount=0;
for(var p in obj1){
pcount++;
if(!isEqual(obj1[p],obj2[p])){
return false;
}
}
for(var p in obj2){
pcount--;
}
return pcount==0;
}else if(typeof(obj1)=="function"&&typeof(obj2)=="function"){
if(obj1.toString()!=obj2.toString()){
return false;
}
}else {
if(obj1!=obj2){
return false;
}
}
}else{
return false;
}
return true;
}
var temp=arr.slice(0);//数组复制一份到temp
for(var i=0;i<temp.length;i++){
for(j=i+1;j<temp.length;j++){
if(isEqual(temp[j],temp[i])){
temp.splice(j,1);//删除该元素
j--;
}
}
}
return temp;
}

测试:


var testArr3=[1,2,2,{a:1},{a:1},{a:1,b:2},function(){alert("b");},function(){alert("b");}];


console.log(distinctArrayAll(testArr3));


//结果 [1,2,{a:1},{a:1,b:2},function(){alert("b");}]

终于顺利完成了去重任务。关于每种方法的性能差异,我们将留待后续讨论。可以看出,最后一种方法是通用的去重方案,能够处理复杂数组,但相应的执行开销也较大。在实际项目开发中,有时我们可能只需要对纯数字或纯字符串进行去重,这就要求我们根据具体需求灵活选择算法,不求绝对完美,但求在满足需求的前提下提高程序效率!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章