目录
一、利用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会被去(忽略)掉,直接消失了,其他都可以去重,包括null。indexOf() 方法无法识别数组的 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 和 对象 无法去重,其他都可以实现去重,包括null。indexOf() 方法无法识别数组的 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的识别问题
数组去重总结 | 总结多方法数组去重与优化