我们之前看过了 手写数组去重, 扁平化函数 , 以及 不产生新数组, 删除数组里的重复元素 , 这里再次加深一下, 如果是数组元素包含对象等类型, 又该如何去重喃?
示例如下:
如传入的数组元素为
[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}]
来源: http://www.jianshu.com/p/246c7967eb5c