我们之前看过了 手写数组去重、扁平化函数 ,以及 不产生新数组,删除数组里的重复元素 ,这里再次加深一下,如果是数组元素包含对象等类型,又该如何去重喃?
示例如下:
- 如传入的数组元素为
[123, "meili", "123", "mogu", 123]
,则输出:[123, "meili", "123", "mogu"]
- 如传入的数组元素为
[123, [1, 2, 3], [1, "2", 3], [1, 2, 3], "meili"]
,则输出:[123, [1, 2, 3], [1, "2", 3], "meili"]
- 如传入的数组元素为
[123, {a: 1}, {a: {b: 1}}, {a: "1"}, {a: {b: 1}}, "meili"]
,则输出:[123, {a: 1}, {a: {b: 1}}, {a: "1"}, "meili"]
基础:JSON.stringify 去重
const removeDuplicates = (arr) => {
let map = new Map();
arr.forEach((item) => {
map.set(JSON.stringify(item), item);
});
return [...map.values()];
};
// 测试
removeDuplicates([123, "meili", "123", "mogu", 123]);
// [123, "meili", "123", "mogu"]
removeDuplicates([123, [1, 2, 3], [1, "2", 3], [1, 2, 3], "meili"]);
// [123, [1, 2, 3], [1, "2", 3], "meili"]
removeDuplicates([
123,
{ a: 1 },
{ a: { b: 1 } },
{ a: "1" },
{ a: { b: 1 } },
"meili",
]);
// [123, {a: 1}, a: {b: 1}, {a: "1"}, "meili"]
深入一下
使用 JSON.stringify
,如果数组元素是 object
类型且里面键的顺序不同则会认为是两个不同放入数组元素
let o1 = { a: 1, b: 2 };
let o2 = { b: 2, a: 1 };
JSON.stringify(o1);
// "{"a":1,"b":2}"
JSON.stringify(o2);
// "{"b":2,"a":1}"
JSON.stringify(o1) === JSON.stringify(o2);
// false
解决思路:
一个数组(包含对象等类型元素)去重函数,需要在基础类型判断相等条件下满足以下条件:
- 如果元素是数组类型,则需要数组中的每一项相等
- 如果元素是对象类型,则需要对象中的每个键值对相等
去重本身就是遍历数组,然后比较数组中的每一项是否相等而已,所以关键步骤有两步:比较、去重
比较:
- 首先判断类型是否一致,类型不一致则返回认为两个数组元素是不同的,否则继续
- 如果是数组类型,则递归比较数组中的每个元素是否相等
- 如果是对象类型,则递归比较对象中的每个键值对是否相等
- 否则,直接
===
比较
去重:
- 采用
reduce
去重,初始accumulator
为[]
- 采用
findIndex
找到accumulator
是否包含相同元素,如果不包含则加入,否则不加入 - 返回最终的
accumulator
,则为去重后的数组
代码实现:
// 获取类型
const getType = (function () {
const class2type = {
"[object Boolean]": "boolean",
"[object Number]": "number",
"[object String]": "string",
"[object Function]": "function",
"[object Array]": "array",
"[object Date]": "date",
"[object RegExp]": "regexp",
"[object Object]": "object",
"[object Error]": "error",
"[object Symbol]": "symbol",
};
return function getType(obj) {
if (obj == null) {
return obj + "";
}
// javascript高级程序设计中提供了一种方法,可以通用的来判断原始数据类型和引用数据类型
const str = Object.prototype.toString.call(obj);
return typeof obj === "object" || typeof obj === "function"
? class2type[str] || "object"
: typeof obj;
};
})();
/**
* 判断两个元素是否相等
* @param {any} o1 比较元素
* @param {any} o2 其他元素
* @returns {Boolean} 是否相等
*/
const isEqual = (o1, o2) => {
const t1 = getType(o1);
const t2 = getType(o2);
// 比较类型是否一致
if (t1 !== t2) return false;
// 类型一致
if (t1 === "array") {
// 首先判断数组包含元素个数是否相等
if (o1.length !== o2.length) return false;
// 比较两个数组中的每个元素
return o1.every((item, i) => {
// return item === target
return isEqual(item, o2[i]);
});
}
if (t2 === "object") {
// object类型比较类似数组
const keysArr = Object.keys(o1);
if (keysArr.length !== Object.keys(o2).length) return false;
// 比较每一个元素
return keysArr.every((k) => {
return isEqual(o1[k], o2[k]);
});
}
return o1 === o2;
};
// 数组去重
const removeDuplicates = (arr) => {
return arr.reduce((accumulator, current) => {
const hasIndex = accumulator.findIndex((item) => isEqual(current, item));
if (hasIndex === -1) {
accumulator.push(current);
}
return accumulator;
}, []);
};
// 测试
removeDuplicates([
123,
{ a: 1 },
{ a: { b: 1 } },
{ a: "1" },
{ a: { b: 1 } },
"meili",
{ a: 1, b: 2 },
{ b: 2, a: 1 },
]);
// [123, {a: 1}, a: {b: 1}, {a: "1"}, "meili", {a: 1, b: 2}]
网友评论