题目描述
编写一个Javascript函数,传入一个数组,对数组中的元素进行去重并返回一个无重复元素的数组,数组的元素可以是数字、字符串、数组和对象。举例说明:
1. 如传入的数组元素为[123, "meili", "123", "mogu", 123],则输出:[123, "meili", "123", "mogu"]
2. 如传入的数组元素为[123, [1, 2, 3], [1, "2", 3], [1, 2, 3], "meili"],则输出:[123, [1, 2, 3], [1, "2", 3], "meili"]
3. 如传入的数组元素为[123, {a: 1}, {a: {b: 1}}, {a: "1"}, {a: {b: 1}}, "meili"],则输出:[123, {a: 1}, {a: {b: 1}}, {a: "1"}, "meili"]
第一种解法:map+Set
把数组中的对象解析成字符串的方法用的是JSON.stringify()方法
var arr1 = [123, {a: 1}, {a: {b: 1}}, {a: "1"}, {a: {b: 1}}, "meili"];
var arr2 = [123, "meili", "123", "mogu", 123];
var arr3 = [123, [1, 2, 3], [1, "2", 3], [1, 2, 3], "meili"];
function unique(arr) {
var b = arr.map(item=>{
return JSON.stringify(item);//JSON.stringify(item)是把数组中的对象解析成字符串再比较
})
var c = Array.from(new Set(b));//Set(b)用来对b去重,但是去重后的结果不是我们想要的数组的形式,所以需要用 Array.from()的方法变成我们想要的数组的形式
var d = c.map(item=>{
return JSON.parse(item);
})
return d;
console.log(unique(arr1));
console.log(unique(arr2));
console.log(unique(arr3));
1.1 filter
使用filter代替map,结果出来的结果不是想要的
注意:filter返回的是filter返回的是在一定条件下返回值为true的原数组项,所以此处最适合的是map或者for循环
所以如果需要从原数组中返回满足一定条件的数组,可以使用filter。
1.2 map和filter
红宝书中是这样描述的:
- filter()----创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
- map()----返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。按照原始数组元素顺序依次处理元素。
1.3 map和foreach
相同点
- 都是会遍历数组的每一个元素,
- 同样是三个参数
- 都是只能遍历数组
不同点
- forEach()方法不会返回执行结果,而是undefined,红宝书中说的是没有返回值。
- 而map()方法会得到一个新的数组并返回
使用场景
- forEach适合于你并不打算改变数据的时候,而只是想用数据做一些事情 – 比如存入数据库或则打印出来
- map()适用于你要改变数据值的时候。不仅仅在于它更快,而且返回一个新的数组
forEach到底会不会改变原数组
1.4 Array.from
Array.from
方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)
1.4.1 将两类对象转化为真正的数组
-
类数组对象所谓类似数组的对象,本质特征只有一点,即必须有
length
属性let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; // ES5的写法 var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c'] // ES6的写法 let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
关于call的思考
-
只要是部署了 Iterator 接口的数据结构,
Array.from
都能将其转为数组。Array.from('hello') // ['h', 'e', 'l', 'l', 'o'] let namesSet = new Set(['a', 'b']) Array.from(namesSet) // ['a', 'b']
补充:扩展运算符(
...
)也可以将某些数据结构转为数组。扩展运算符背后调用的是遍历器接口(Symbol.iterator
),因此只要具有 Iterator 接口的对象,都可以使用扩展运算符。let nodeList = document.querySelectorAll('div'); let array = [...nodeList];
上面代码中,
querySelectorAll
方法返回的是一个NodeList
对象。它不是数组,而是一个类似数组的对象。这时,扩展运算符可以将其转为真正的数组,原因就在于NodeList
对象实现了 Iterator- 比如 Map 结构是具有 Iterator 接口的对象,可以使用扩展运算符
let map = new Map([ [1, 'one'], [2, 'two'], [3, 'three'], ]); let arr = [...map.keys()]; // [1, 2, 3]
- Generator 函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。
const go = function*(){ yield 1; yield 2; yield 3; }; [...go()] // [1, 2, 3]
上面代码中,变量
go
是一个 Generator 函数,执行后返回的是一个遍历器对象,对这个遍历器对象执行扩展运算符,就会将内部遍历得到的值,转为一个数组。- 如果对没有 Iterator 接口的对象,使用扩展运算符,将会报错。
const obj = {a: 1, b: 2}; let arr = [...obj]; // TypeError: Cannot spread non-iterable object
-
本身是数组的话
Array.from([1, 2, 3]) // [1, 2, 3]
1.4.2 Array.from还可以接受第二个参数
作用类似于数组的
map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]
1.4.3 如果map
函数里面用到了this
关键字,还可以传入Array.from
的第三个参数,用来绑定this
。
1.4.4 将字符串转为数组,然后返回字符串的长度
function countSymbols(string) {
return Array.from(string).length;
}
第二种解法 :双重遍历
思想:通过拿出一个元素和剩下的元素依次比较,如果全部不相等则证明此元素为唯一
var arr1 = [123, {a: 1}, {a: {b: 1}}, {a: "1"}, {a: {b: 1}}, "meili"];
var arr3 = [123, "meili", "123", "mogu", 123];
var arr2 = [123, [1, 2, 3], [1, "2", 3], [1, 2, 3], "meili"];
function unique(arr) {
let b=[]
for(let i=0;i<arr.length;i++){
let unexit=true
for(let j=i+1;j<arr.length;j++){
if(JSON.stringify(arr[i])===JSON.stringify(arr[j])){
unexit=false
break
}
else{
unexit=true
}
}
if(unexit){
b.push(arr[i])
}
}
return b
}
console.log(unique(arr1));
console.log(unique(arr2));
console.log(unique(arr3));
缺点:顺序会改变
第三种解法:Object.keys():存在唯一性
var arr1 = [123, {a: 1}, {a: {b: 1}}, {a: "1"}, {a: {b: 1}}, "meili"];
var arr3 = [123, "meili", "123", "mogu", 123];
var arr2 = [123, [1, 2, 3], [1, "2", 3], [1, 2, 3], "meili"];
function unique(arr) {
let b=[]
let hash={}
for(let i=0;i<arr.length;i++){
if(!hash[JSON.stringify(arr[i])]){
hash[JSON.stringify(arr[i])]=true
b.push(arr[i])
}
}
return b
}
console.log(unique(arr1));
console.log(unique(arr2));
console.log(unique(arr3));
这三种方法都可以实现对象去重,第二种存在一些缺点,顺序会改变。
参考:https://www.jb51.net/article/134411.htm