JS数组怎么去重?| JavaScript中数组去重的14种方法

news2024/11/18 8:52:35

目录

一、利用for循环嵌套去重

二、利用splice() + for循环嵌套(ES5中最常用)

三、利用indexOf() + for循环去重

四、利用sort() + for循环去重

五、利用includes() + for循环去重(ES7)

六、利用Object键值对及其hasOwnProperty()去重

七、利用filter() + indexOf()去重

八、利用递归去重

九、利用Set数据结构(ES6)

十、利用Map数据结构去重(ES6)

十一、利用Map数据结构 + filter()

十二、利用reduce() + includes()去重

十三、利用findIndex() + Object.is()去重

十四、对象数组去重 - 🌤️ 

🌹 方法总结

💌 写在最后


一、利用for循环嵌套去重

var array = [ 0, 0, 1, 1, '1', '1', 'true', 'true', true, true, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 'a', 'a', {}, {}, { a: 1 }, { b: 2 }, { a: 1 }];

function unique(array) {
    // res用来存储结果
    var res = [];
    for (var i = 0, arrayLen = array.length; i < arrayLen; i++) {
        for (var j = 0, resLen = res.length; j < resLen; j++ ) {
            if (array[i] === res[j]) {
                break;
            }
        }
        // 如果array[i]是唯一的,那么执行完循环,j等于resLen
        if (j === resLen) {
            res.push(array[i])
        }
    }
    return res;
}
// NaN 和 对象 无法去重
console.log(unique(array)); 
// [0, 1, '1', 'true', true, false, undefined, null, NaN, NaN, 'NaN', 'a', {…}, {…}, {…}, {…}, {…}]



输出结果:NaN 和 对象 无法去重,其他都可以去重(包括null)

我的结论:for循环 判断依据是全等===——值和类型都相同,才去重


二、利用splice() + for循环嵌套(ES5中最常用)

利用for循环嵌套,然后splice去重:将数组中的每一个元素依次与其他元素作比较,发现重复元素,删除。

var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];

function unique(arr) {
  for (var i = 0; i < arr.length; i++) {
    for (var j = i + 1; j < arr.length; j++) {
      if (arr[i] == arr[j]) {
        //如果第一个等同于第二个,splice方法删除第二个
        arr.splice(j, 1);

        j--;
      }
    }
  }
  return arr;
}

console.log(arr[4],typeof arr[4],arr[6],typeof arr[6]) // true string true boolean
console.log(arr[4]==1,arr[6]==1) // false true

// NaN 和 {} 没有去重,且两个 null 直接消失了
// 如果第一个等同于第二个,splice方法删除第二个,所以1跟'1'、true留1,false跟0留false
console.log(unique(arr)); // [1, 2, 'true', 15, false, undefined, NaN, NaN, 'NaN', 'a', {…}, {…}, {…}, {…}, {…}]

双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。

输出结果:NaN 和 对象 无法去重,且null会被忽略掉,直接消失了

我的结论:splice() 判断依据是值等==——值相同

字符串类型的数字、boolean类型:true、false,比较时会转为number类型的数字


三、利用indexOf() + for循环去重

indexOf() 方法可返回数组中某个指定的元素位置。

该方法将从头到尾地检索数组,看它是否含有对应的元素。开始检索的位置在数组 start 处或数组的开头(没有指定 start 参数时)。如果找到一个 item,则返回 item 的第一次出现的位置。开始位置的索引为 0。

如果在数组中没找到指定元素则返回 -1。

提示:如果你想查找字符串最后出现的位置,请使用 lastIndexOf() 方法。


JavaScript Array indexOf() 方法 | 菜鸟教程

PS:JavaScript String 对象中的 indexOf() 与 lastIndexOf() 方法的用法与此类似

JavaScript indexOf() 方法 | 菜鸟教程


注意:JavaScript String 对象中的 indexOf() 与 lastIndexOf() 方法区分大小写。

新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。

输出结果:NaN 和 对象 无法去重indexOf() 方法无法识别数组的 NaN 和 {} 成员。

我的结论:indexOf() 判断依据是全等===——值和类型都相同,才去重


四、利用sort() + for循环去重

sort() 方法用于对数组的元素进行排序。

排序顺序可以是字母或数字,并按升序或降序。

◾ 默认排序顺序为按字母升序。

注意:当数字是按字母顺序排列时"40"将排在"5"前面。

◾ 使用数字排序,你必须通过一个函数作为参数来调用。

函数指定数字是按照升序还是降序排列。

注意: 这种方法会改变原始数组!

利用sort()排序方法,然后根据排序后的结果进行遍历及相邻元素比对。 

var array = [1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];
function unique(array) {
    if(!Array.isArray(array)) {
        console.log('type error');
        return;
    }
    arr = array.sort(); // 对array进行排序,sort()会改变原数组
    var arrys = [arr[0]]; // 新建一个数组,用于存放不包含重复元素的数组
    for(var i = 1; i < arr.length; i++) {
        if(arr[i] != arr[i-1]) {
            arrys.push(arr[i]);
        }
    }
    return arrys;
}
// NaN 和 对象 无法去重,其他都可以去重(包括null)
console.log(unique(array)); // [0, 1, 15, 2, NaN, NaN, 'NaN', {…}, {…}, {…}, {…}, {…}, 'a', false, null, 'true', true, undefined]

输出结果:NaN 和 对象 无法去重,其他都可以去重(包括null)

我的结论:sort()  判断依据是全等===——值和类型都相同,才去重


五、利用includes() + for循环去重(ES7)

includes() 方法用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false。

JavaScript Array includes() 方法 | 菜鸟教程

var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];
function unique(arr) {
  if (!Array.isArray(arr)) {
    console.log("type error!");
    return;
  }

  var array = [];
  for (var i = 0; i < arr.length; i++) {
    if (!array.includes(arr[i])) { // includes 检测数组是否有某个值
      array.push(arr[i]);
    }
  }
  return array;
}

// 对象 无法去重,其他都可以去重(包括NaN、null)
console.log(unique(arr)); // [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]

输出结果:对象 无法去重,其他都可以去重(包括NaN、null)

我的结论:includes() 判断依据是全等===——值和类型都相同,才去重

PS:JavaScript String 对象也有 includes() 方法。JavaScript includes() 方法 | 菜鸟教程

includes() 方法用于判断字符串是否包含指定的子字符串。如果找到匹配的字符串则返回 true,否则返回 false。

注意:JavaScript String 对象的 includes() 方法区分大小写。


六、利用Object键值对及其hasOwnProperty()去重

◾ hasOwnProperty方法

hasOwnProperty() 方法是 Object 的原型方法(也称实例方法),它定义在 Object.prototype 对象之上,所有 Object 的实例对象都会继承 hasOwnProperty() 方法。

hasOwnProperty() 方法用来检测一个属性是否是对象的自有属性,而不是从原型链继承的。如果该属性是自有属性,那么返回 true,否则返回 false。

换句话说,hasOwnProperty() 方法不会检测对象的原型链,只会检测当前对象本身,只有当前对象本身存在该属性时才返回true。

用法:object.hasOwnProperty(propertyName)  ,参数propertyName指要检测的属性名;

◾ Object 键值对:var obj = {...}

—— 如何实现去重 ——

1、通过 filter 为数组的每一个元素做条件过滤。

2、再通过 三元运算 将数组的元素作为key登记在obj中,将它对应的值设为true。

3、通过 hasOwnProperty 判断对象是否包含某一属性(键),如果包含即返回false, filter 接收到false将会过滤掉该 item (array[index]) 的return事件。

Object 键值对这种方法是利用一个空的 Object 对象,我们把数组的值存成 Object 的 key 值,比如 Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。示例代码如下:

var array = [1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];

function unique(array) {
    var obj = {};
    return array.filter(function(item, index, array){
        return obj.hasOwnProperty(item) ? false : (obj[item] = true)
    })
}
// 可去重同值不同类型的项,但对象 无法去重,且原数组中除第一个对象外,所有对象都会被去(忽略)掉;
console.log(unique(array)); // [1, 2, 'true', 15, false, undefined, null, NaN, 0, 'a', {…}]



输出结果:Object 键值对方法的有2个bug会去重同值不同类型的项,无法去重(区分)多个对象,原数组中除第一个对象可以去重外,所有对象都会被去(忽略)掉,

缺点总结:

bug - 1 会去重同值不同类型的项

由于Object的键只能是String类型,因此 obj[1] 与 obj['1'] 以及 obj['true'] 与 obj[true]、obj[NaN] 与 obj['NaN'] 是等价的,它们引用同一个堆栈,最终在第二数组实参中 1 和 '1' 、'true' 和 true、NaN 和 'NaN' 被去重为同一个元素。

bug -2 原数组中除第一个对象可以去重外,所有对象都会被去(忽略)掉  ——无法去重多个对象

改进1.0:typeOf - 升级版

分析:我们可以发现,由于Object的键只能是String类型,这样做是有问题的,因为 1 和 '1' 是不同的,但是这种方法会判断为同一个值,这是因为对象的键值只能是字符串,所以我们可以通过使用 typeof item + item 的方式拼成一个字符串,来作为obj的 key 值来避免这个问题

var arr = [1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{}];

function unique(arr) {
  var obj = {};
  return arr.filter(function (item, index, arr) {
    // 使用typeof item+item拼成一个字符串,来作为 hasOwnProperty 的判断依据以及obj的key值
    return obj.hasOwnProperty(typeof item + item)
      ? false
      : (obj[typeof item + item] = true);
  });
}
// 都可以去重,包括NaN、null、{}(仅限数组中的一个对象,如有其他对象都会被去掉)
console.log(unique(arr)); // [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}]



输出结果:升级版存在1个bug:无法去重(区分)多个对象,原数组中除第一个对象可以去重外,所有对象都会被去(忽略)掉;

缺点总结:无法去重(区分)多个对象

bug - 对象去重,仅限原数组中的第一个对象,其他所有对象都会被去(忽略)掉;

如果数组中存在多个对象,除了数组中的第一个对象,所有的对象都会被过滤不管该对象是否重复都会被去掉)。 

——分析去重过程 ——

使用filter函数,过滤出一个新数组,也就是最终的去重后的数组。

三元表达式,主要是为了得到判断结果:true或false,然后return这个true或false这个结果,以便filter函数过滤出一个不包含重复项的新数组

① 当obj.hasOwnProperty(typeof item + item)为 false 时,说明obj对象中不存在(typeof item + item)这个自有属性,也就是说当前循环的arr中的item项的值,没有让typeof item + item成为obj的私有属性,obj不存在(typeof item + item)这个属性

此时,整个三元表达式的值为(obj[typeof item + item] = true),整个值的真假(true 或 flase)决定了filter函数当前循环的item值是否能成为filter过滤出的新数组的元素。

因为目前obj中不存在(typeof item + item)这个自有属性,所以obj[ typeof item + item ] =undefined,所以undefined = true为true,说明当前循环的arr中的item项,符合条件,会加入filter函数过滤后的新数组

② 当obj.hasOwnProperty(typeof item + item)为 true 时,说明obj对象中已存在(typeof item + item)这个自有属性,

此时,三元表达式的值为false说明当前循环的arr中的item项,不符合条件,不会加入filter函数过滤后的新数组

举个例子:array[0]和array[4]分别被 typeof 拼接成 "number1" 和 "stringtrue",很好的区分了不同类型转字符串后的区别

但是如果数组中存在对象,比如[ { name: 97 }, { descript: 'z' } ],由于 typeof item + item 的结果都会是 object[object Object] (String类型),所以除了数组中的第一个对象,所有的对象都会被过滤(不重复的对象也会被去掉)

改进2.0 :JSON.stringify() - 终极解决方案!

然而,即便如此,我们依然无法正确区分出两个对象,比如 {value: 1} 和 {value: 2},因为 object[typeof item + item] 的结果都会是 object[object Object],所以我们可以使用 JSON.stringify() 将对象序列化,来避免相同的键值对。

let arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];

let obj={}

let result=arr.filter((item)=>{
   return obj.hasOwnProperty(
   //因为JSON.stringify(NaN)==null,所以先检测是否为数字类型,数字类型直接返回,否则再JSON.stringify
    typeof item=="number"?item:JSON.stringify(item))?
    false:(obj[typeof item=="number"?item:JSON.stringify(item)]=1);
})

// 全都可以去重,完美解决方案!
console.log(result) // [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {}, {a:1}, {b:2}]



输出结果:全都可以去重,无bug,完美解决方案! 

我的结论:Object键值对 + hasOwnProperty() + typeOf + JSON.stringify() 判断依据是全等===——值和类型都相同,才去重


七、利用filter() + indexOf()去重

番外:关于数组的 indexOf() 方法JavaScript Array indexOf() 方法 | 菜鸟教程

用法:array.indexOf(item,start) 

start:可选的整数参数。规定在数组中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略该参数,则将从字符串的首字符开始检索。

var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN,NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];

function unique(arr) {
  return arr.filter(function (item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    return arr.indexOf(item, 0) === index;
  });
}
// 
console.log(unique(arr)); // [1, 2, '1', 'true', true, 15, false, undefined, null, 'NaN', 0, 'a', {} , {} , {a:1} , {b:2} , {a:1}]

输出结果:对象 无法去重,NaN会被去(忽略)掉,直接消失了,其他都可以去重,包括nullindexOf() 方法无法识别数组的 NaN 和 {} 成员。

我的结论:filter() 判断依据是全等===——值和类型都相同,才去重


八、利用递归去重

var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];

function unique(arr) {
  var array = arr;
  var len = array.length;
  array.sort(function (a, b) { // 排序后更加方便去重
    return a - b;
  })

  function loop(index) {
    if (index >= 1) {
      if (array[index] === array[index - 1]) {
        array.splice(index, 1);
      }
      loop(index - 1); // 递归loop,然后数组去重
    }
  }
  loop(len - 1);
  return array;
}
// 
console.log(unique(arr)) // [1, '1', true, 2, 'true', false, null, 0, true, 15, NaN, NaN, 'NaN', 'a', {…}, {…}, {…}, {…}, {…}, undefined]


输出结果:NaN 和 对象 无法去重,其他都可以实现去重,包括nullindexOf() 方法无法识别数组的 NaN 和 {} 成员。

我的结论:递归去重法的判断依据是全等===——值和类型都相同,才去重


九、利用Set数据结构(ES6)

随着 ES6 的到来,去重的方法又有了进展,比如我们可以使用 Set 和 Map 数据结构,以 Set 为例,ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。

因为Set是一个类似数组结构,所以需要用 Array.from() 方法解析类数组为数组,将其转型为真正的数组去使用。Array.from 方法可以将 Set 结构转为数组。

var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];
 
function unique (arr) {
  return Array.from(new Set(arr))
}
// 对象 无法去重,会被保留下来,但可对 NaN、null 去重
console.log(unique(arr)) 
//[1, 2, '1', "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {} , {a:1} , {b:2} , {a:1}]


甚至可以再简化下:语法糖——复制一个数组: var arr2=[...arr1]

var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];

function unique(arr) {
    // 利用 [...new Set(arr)] 转为数组后去重
    return [...new Set(arr)];
}
console.log(unique(arr));
// [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]

还可以再简化下:转为ES6 箭头函数

var arr = [1, 1, 2, '1', 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}, { a: 1 }, { b: 2 }, { a: 1 }];

var unique = (arr) => [...new Set(arr)]

console.log(unique(arr));
// (18) [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]

注:不考虑兼容性,这种使用Set数据结构去重的方法代码最少。而且这种方法还无法去重对象,不过可以对NaN、null进行去重,因为 Set 加入值时认为NaN等于自身,后面的高阶方法会添加去掉重复“{}”的方法。

输出结果:对象 无法去重,其他都可以实现去重,包括NaN、null 

我的结论:newSet() 判断依据是全等===——值和类型都相同,才去重


十、利用Map数据结构去重(ES6

了解Map数据结构

Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。

Map 与 Object 的区别:Object里的属性是字符串;Map里的key可以是任意的数据类型 key:value

对Map来说,我们可以通过使用set,get,has,delete等方法来对Map进行操作

参考:JS中Map数据结构,看这一篇就够了 | Set 和 Map 数据结构_阮一峰

参考:JS中的内置对象和数据结构

创建一个空Map数据结构,遍历需要去重的数组,把数组的每一个元素作为key存到Map中,由于Map中不会出现相同的key值,所以,最终得到的就是去重后的结果。

var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];

function unique(arr) {
  // Map与Object的区别 Object里的属性是字符串;Map里的key可以是任意的数据类型 key:value
  let map = new Map();
  let array = new Array(); // 数组用于返回结果
  for (let i = 0; i < arr.length; i++) {
    // has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
    if (map.has(arr[i])) { // 如果有该key值
      // set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
      map.set(arr[i], true);
    } else {
      map.set(arr[i], false); // 如果没有该key值
      array.push(arr[i]);
    }
  }
  return array;
}
// 
console.log(unique(arr)) 
// [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]



输出结果:对象 无法去重,其他都可以实现去重,包括NaN、null 

我的结论:Map数据结构去重法 判断依据是全等===——值和类型都相同,才去重


十一、利用Map数据结构 + filter()

var arr = [1, 1, 2, '1', 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}, { a: 1 }, { b: 2 }, { a: 1 }];

function unique(arr) {
  const seen = new Map()
  return arr.filter((item) => !seen.has(item) && seen.set(item, 1))
}

console.log(unique(arr));
// [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]



输出结果:对象 无法去重,其他都可以实现去重,包括NaN、null 

我的结论:Map数据结构去重法 判断依据是全等===——值和类型都相同,才去重


十二、利用reduce() + includes()去重

Array.reduce 为数组的归并方法,使用场景很多,比如求和(累加)、求乘积(累乘),计次,去重,多维转一维,属性求和等...

var arr=[1,1,2,'1','true','true',true,true,15,15,false,false,undefined,undefined,null,null,NaN, NaN,'NaN',0,0,'a','a',{},{},{a:1},{b:2},{a:1}];
function unique(arr) {
  return arr.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], []);
}

// 对象 无法去重
console.log(unique(arr));
// [1, 2, '1', 'true', true, 15, false, undefined, null, NaN, 'NaN', 0, 'a', {…}, {…}, {…}, {…}, {…}]

输出结果:对象 无法去重,其他都可以实现去重,包括NaN、null 

我的结论:reduce() + includes()去重法 判断依据是全等===——值和类型都相同,才去重


十三、利用findIndex() + Object.is()去重

—— findIndex() ——

findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。

findIndex() 方法为数组中的每个元素都调用一次函数执行:

  • 当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,之后的值不会再调用执行函数。
  • 如果没有符合条件的元素返回 -1

注意: findIndex() 对于空数组,函数是不会执行的。

注意: findIndex() 并没有改变数组的原始值。

参考:JavaScript findIndex() 方法 | 菜鸟教程 

—— Object.is() ——

Object.is() 方法用来判断两个值是否相等,它接收两个参数,分别是需要比较的两个值。

返回一个 Boolean 值表示这两个值是否相等。

参考:Object.is() - JavaScript | MDN | 你用过Object.is() 方法吗?

var arr = [1, 1, 2, '1', 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}, { a: 1 }, { b: 2 }, { a: 1 }];

function unique(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    // Object.is()方法认为NaN等于自身
    if (result.findIndex(value => Object.is(arr[i],value)) === -1) {
      result.push(arr[i]);
    }
  }
  return result;
}
// 
console.log(unique(arr));

输出结果:对象 无法去重,其他都可以实现去重,包括NaN、null 

我的结论:reduce() + includes()去重法 判断依据是全等===——值和类型都相同,才去重


十四、对象数组去重 - 🌤️ 

方法一:使用Map数据结构

Map是一组键值对的结构,用于解决以往不能用对象做为键的问题,具有极快的查找速度。(注:函数、对象、基本类型都可以作为键或值。

参考:ES6中的Map数据结构 | Map 和 Set 的特点和区别

const ary = [{id: 1, text: "1"}, {id: 1, text: "1"}, {id: 2, text: "2"}, {id: 3, text: "3"}];

const myseta = (ary) => {
  const map = new Map();
  return ary.filter((ary) => !map.has(ary.id) && map.set(ary.id, 1))
};

console.log(myseta(ary)); // [{id: 1, text: '1'}, {id: 2, text: '2'}, {id: 3, text: '3'}]

方法二:使用Set数据结构

JSON.stringify + new Set( ) + Array.from() + JSON.parse

使用Set数据结构去除重复对象,必须把对象转为string字符串形式,才可以实现对象去重,所以需要new Set(strings)进行转型。new Set去重对象 - 简书

因为Set数据结构并非真正的数组,它类似于数组,并且成员值都是唯一的,没有重复,所以可以用来做去重操作。但是因为它是一个类似数组结构,所以需要转型为真正的数组去使用。所以,需要用Array.from

const ary = [{id: 1, text: "1"}, {id: 1, text: "1"}, {id: 2, text: "2"}, {id: 3, text: "3"}];
const myseta = (ary) => {
  const strings = ary.map((item) => JSON.stringify(item))
  // 使用Set数据结构去重对象
  // return new Set(strings)

  // 使用Array.from()把Set数据结构去重对象后的结构,转为数组
  // return Array.from(new Set(strings))

  // 使用Array.from()转为数组,然后再使用数组的map方法把数组里面的字符串类型转化为对象类型:
  return Array.from(new Set(strings)).map((item) => JSON.parse(item))

};

// console.log(myseta(ary)); // Set(3) {'{"id":1,"text":"1"}', '{"id":2,"text":"2"}', '{"id":3,"text":"3"}'}
// console.log(myseta(ary)); // ['{"id":1,"text":"1"}', '{"id":2,"text":"2"}', '{"id":3,"text":"3"}']
console.log(myseta(ary)); // [{"id":1,"text":"1"}', '{"id":2,"text":"2"}', '{"id":3,"text":"3"}]

番外:JavaScript Array map() 方法

定义和用法

map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

map() 方法按照原始数组元素顺序依次处理元素。

注意: map() 不会对空数组进行检测。

注意: map() 不会改变原始数组。


🌹 方法总结

如果有如下这样一个数组 :

var arr = [ 0, 0, 1, 1, '1', '1', 'true', 'true', true, true, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 'a', 'a', new String('1'), new String('1'), /a/, /a/, {}, {}, { a: 1 }, { b: 2 }, { a: 1 }];

由于各去重方法的兼容性不同,所得到的去重结果就会有所有不同,下面来查看一下各方法的兼容性差异: 

方法结果说明
for循环[0, 1, '1', 'true', true, false, undefined, null, NaN, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]❌NaN 和 对象、正则、构造函数不去重;
splice() + for循环[0, 1, 'true', undefined, NaN, NaN, 'NaN', 'a', /a/, /a/, {…}, {…}, {…}, {…}, {…}]❌NaN 和 对象、正则不去重;null、构造函数被忽略掉;字符串数字、boolean的true、false转为数字
indexOf() + for循环[0, 1, '1', 'true', true, false, undefined, null, NaN, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]❌NaN 和 对象、正则、构造函数不去重;
sort() + for循环[/a/, /a/, 0, 1, String, NaN, NaN, 'NaN', {…}, {…}, {…}, {…}, {…}, 'a', false, null, 'true', true, undefined]❌NaN 和 对象、正则不去重;new String('1') 和 ‘1’等价,所以会去重
includes() [0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]❌对象、正则、构造函数不去重
优化后的键值对方法[0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', /a/, {…}, {…}]⭕全去重,完美解决方案!
filter()+indexOf()[0, 1, '1', 'true', true, false, undefined, null, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]❌对象、正则、构造函数不去重;NaN会被忽略掉
递归[0, false, null, 1, '1', 'true', true, NaN, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}, undefined]❌NaN 和 对象、正则、构造函数不去重;
Set数据结构[0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]❌对象、正则、构造函数不去重;
Map数据结构[0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]❌对象、正则、构造函数不去重;
Map数据结构 + filter()[0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]❌对象、正则、构造函数不去重;

reduce() + includes()

[0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]❌对象、正则、构造函数不去重;
findIndex() + Object.is()[0, 1, '1', 'true', true, false, undefined, null, NaN, 'NaN', 'a', String, String, /a/, /a/, {…}, {…}, {…}, {…}, {…}]❌对象、正则、构造函数不去重;

注意:上述表格的说明部分,除了不去重的、被去重跳过忽略掉的之外,其他的都可以实现去重,包括null、NaN(当然,仅限在去重数组范围内的项,不在测试去重数组范围内其他类型数据,大家可自行测试。),以上方法的整理总结,如有错误之处,还请大家评论指出!

💌 写在最后

虽然各个方法去重的结果有所不同,但更重要的是让我们知道在合适的场景要选择合适的去重方法。

另外,还有很多文章提到其他去重的方法,比如,foreach + indexOf 数组去重的方法,不过,实现的过程,个人觉得都是大同小异。如果还有其他比较好的去重方法,欢迎评论给出!


◾ 相关参考资料

JS中对数组去重 - NaN去重问题 | 数组去重 - NaN的识别问题

数组去重总结 | 总结多方法数组去重与优化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1720903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在phpstorm2024版里如何使用Jetbrains ai assistant 插件 ?

ai assistant激活成功后&#xff0c;如图 ai assistant渠道&#xff1a;https://web.52shizhan.cn/activity/ai-assistant 在去年五月份的 Google I/O 2023 上&#xff0c;Google 为 Android Studio 推出了 Studio Bot 功能&#xff0c;使用了谷歌编码基础模型 Codey,Codey 是…

解锁 JavaScript ES6:函数与对象的高级扩展功能

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; ES5、ES6介绍 文章目录 ES6函数扩展1 默认参数1.1 之前写法1.2 ES6 写法1.3 注意点 2 …

python实现——综合类型数据挖掘任务(无监督的分类任务)

综合类型数据挖掘任务 航空公司客户价值分析。航空公司客户价值分析。航空公司客户价值分析。航空公司已积累了大量的会员档案信息和其乘坐航班记录&#xff08;air_data.csv&#xff09;&#xff0c;以2014年3月31日为结束时间抽取两年内有乘机记录的所有客户的详细数据。利用…

python多种方式 保留小数点位数(附Demo)

目录 前言1. 字符串格式2. round函数3. Decimal模块4. numpy库5. Demo 前言 在Python中&#xff0c;保留小数点后特定位数可以通过多种方式实现 以下是几种常见的方法&#xff0c;并附上相应的代码示例&#xff1a; 使用字符串格式化&#xff08;String Formatting&#xff…

Go 1.23新特性前瞻

2024年5月22日&#xff0c;Go 1.23版本[1]功能特性正式冻结&#xff0c;后续将只改bug&#xff0c;不增加新feature。 对Go团队来说&#xff0c;这意味着开始了Go 1.23rc1的冲刺&#xff0c;对我们普通Gopher而言&#xff0c;这意味着是时候对Go 1.23新增的功能做一些前瞻了&am…

ad18学习笔记20:焊盘设置Solder Mask Expansion(阻焊层延伸)

【AD18新手入门】从零开始制造自己的PCB_ad18教程-CSDN博客 Altium Designer绘制焊盘孔&#xff08;Pad孔&#xff09;封装库的技巧&#xff0c;包括原理图封装和PCB封装_哔哩哔哩_bilibili 默认的焊盘中间是有个过孔的&#xff0c;单层焊盘&#xff08;表贴烛盘&#xff09;…

ARM学习(28)NXP 双coreMCU IMX1160学习

笔者最近接触到一块IMXRT1160的双core板子&#xff0c;特依次来记录学习一下 1、IMXRT1160 板子介绍 介绍一下NXP的Demo板子&#xff0c;是一个双core的板子&#xff0c;Cortexm7和Cortexm4&#xff0c;总计1MB的RAM空间&#xff0c;256KB的ROM空间&#xff0c;提供了丰富的内…

【GD32】05 - PWM 脉冲宽度调制

PWM PWM (Pulse Width Modulation) 是一种模拟信号电平的方法&#xff0c;它通过使用数字信号&#xff08;通常是方波&#xff09;来近似地表示模拟信号。在PWM中&#xff0c;信号的占空比&#xff08;即高电平时间占整个周期的比例&#xff09;被用来控制平均输出电压或电流。…

IDEA一键启动多个微服务

我们在做微服务项目开发的时候&#xff0c;每次刚打开IDEA&#xff0c;就需要把各个服务一个个依次启动&#xff0c;特别是服务比较多时&#xff0c;逐个点击不仅麻烦还费时。下面来说一下如何一键启动多个微服务。 操作步骤 点击Edit Configurations 2.点击“”&#xff0c;…

BSD盲区检测系统性能要求及试验方法

相关术语 盲区检测(bsd,blind spot detection)试验车辆(subject vehicle)目标车辆(target vehicle)横向距离(lateral distance):试验车车身最外缘(不包含外后视镜)与目标车辆车身最外缘(不包含外后视镜)之间的横向距离。 纵向距离(longitudinal distance):试验…

用windows server backup备份文件夹到网络共享文件夹并恢复

一、备份 开始 运行windows server backup,在右边的窗格中点击“备份计划” 选择备份配置 因为我们要备份的是一个文件夹&#xff0c;所以&#xff0c;选“自定义”&#xff0c;卷即为磁盘分区。 选择要备份的项 点击添加项目&#xff0c;可依次添加多个备份项目。 勾选需要…

职场思考-阅读专业与技能同步增长(12)

我们可以发现一个人“逆袭”的关键就是先要改变自己的思想&#xff0c;而机遇、资源都是外部条件。 改变思想无非也就几个途径&#xff1a; 一是阅读换脑&#xff0c;二是遇贵人点拨&#xff0c;三是社会“碰壁”后的反思并改正 对于职场进阶而言&#xff0c;阅读更是必要手段 …

知名专业定制线缆知名智造品牌推荐-精工电联:解决水下机器人线缆行业痛点的领航者

在科技日新月异的今天&#xff0c;精工电联作为高科技智能化产品及自动化设备专用连接线束和连接器配套服务商&#xff0c;致力于为高科技行业提供高品质、高性能的集成线缆和连接器定制服务。我们不仅是高品质定制线缆供应商&#xff0c;更是水下机器人线缆行业痛点的有效解决…

Dynamics CRM 修改新建记录的CreatedOn字段值

CRM在创建新记录时&#xff0c;一些系统属性例如创建者、创建时间是取当前创建记录的人以及当前的时间&#xff0c;而有时这些属性需要更改&#xff0c;例如创建时间&#xff0c;这个场景更多的用在数据迁移的时候&#xff0c;老数据有他的原始创建时间&#xff0c;不能因为迁移…

Linux网络-使用Tcp协议进行网络通信并通过网络接口实现远端翻译

文章目录 Tcp协议Tcp协议常见API接口1. int socket(int domain, int type, int protocol);2. int bind(int socket, const struct sockaddr *address, socklen_t address_len);struct sockaddr 3. int listen(int socket, int backlog);4. int accept(int socket, struct socka…

【设计模式】结构型-门面模式

前言 在软件开发中&#xff0c;设计模式是解决特定问题的经验总结&#xff0c;为开发者提供了一种可复用的解决方案。其中&#xff0c;门面模式&#xff08;Facade Pattern&#xff09;是一种结构型模式&#xff0c;旨在为复杂系统提供简化的接口&#xff0c;使客户端与系统之…

Java(十一)---String类型

文章目录 前言1.String类的重要性2.常用方法2.1.字符串的创建2.2.字符串的比较2.2.1.比较是否引用同一个对象2.2.2.boolean equals(Object anObject) 方法&#xff1a;2.2.3.int CompareTo(String s)2.2.4.int compareToIgnoreCase(String str) 方法&#xff1a; 2.3.字符串的查…

算法(十四)动态规划

算法概念 动态规划&#xff08;Dynamic Programming&#xff09;是一种分阶段求解的算法思想&#xff0c;通过拆分问题&#xff0c;定义问题状态和状态之间的关系&#xff0c;使得问题能够以递推&#xff08;分治&#xff09;的方式去解决。动态规划中有三个重点概念&#xff…

C 基础 - 预处理命令和基本语法详解

#include <stdio.h> //预处理指令int main() //函数 {printf("Hello, World!"); //输出语句return 0; //返回语句 } 目录 一.预处理指令 1.#define #ifdef #ifndef #if #else #elif #endif 2.#inlcude a.新增一个文件 b.#include c.运行结果 d.扩…

AI炒股-批量爬取网易财经的要闻板块

工作任务和目标&#xff1a;批量爬取网易财经的要闻板块 在class"tab_body current"的div标签中&#xff1b; 标题和链接在&#xff1a;<a href"https://www.163.com/dy/article/J2UIO5DD051188EA.html">华为急需找到“松弛感”</a> 第一步&…