题目描述
实现一个duplicate
方法,实现数组的去重
特殊要求
- 考虑值类型与引用类型
- 引用类型只考虑普通
Array
与Object
- 可以不考虑Symbol与BigInt
- 不考虑function
{}
与{}
,{a:1,b:2}
与{b:2,a:1}
,[]
与[]
算相等
- 即只要对象的每一项值都相等那么也算相等
- 不能使用set (直接使用set就降低了题目难度)
function duplicate(arr){ // ...code }
题目分析
- 首先需要考虑的值类型有5种
- null
- undefined
- string
- boolean
- number
- number 的话需要特殊考虑
NaN
NaN !== NaN
-->true
- 判断步骤可以先判断类型是否一致,如果一致再判断内容
- 按题目特殊要求判断引用类型是否相等的步骤
- 有朋友可能会说直接
JSON.stringify(obj)
,说明没有考虑周全,对象的键顺序不一致,导致stringify的结果不一致的问题
解题思路
- (笔者)
判断两个引用类型是否值完全一致
的思路
- 判断引用类型
- 数组
- 对象
- 数组
- 判断数组长度是否一致
- 逐项进行比较
- 对象
- 判断目标对象自身属性键的数量是否一致,一致再判断A是否完全包含B的键
- 逐项判断值是否一致
- 去重步骤思路
- 原数组记为
arr
,新数组记为res
- 遍历
arr
与res
的每一项进行比较
- 先判断类型是否一致
- 都是值类型,则
===
比较 - 都是数组,则比较数组的每一项是否一致
- 都是对象,则比较是否拥有相同的键且键的值是一样
分步骤实现
功能函数实现
- 判断类型是否一致
function isSameType(a, b) { // 两者都是值类型 if (typeof a === typeof b && !(a instanceof Object) && !(b instanceof Object)) { return true } // 两者都是对象 if (a instanceof Object && b instanceof Object) { let aOk = a instanceof Array let bOk = b instanceof Array // 都是数组,或者都不是数组则ok --> aOK === bOk return aOk === bOk } return false }
- 判断是否是值/引用类型
因为题目的引用类型只包含Array与Object,所以这里简单使用instanceof判断
function isObject(a){ return a instanceof Object } function isValueType(a){ return !isObject(a) }
- 判断值类型的两者是否值一致
function isSame(a, b) { // 为什么不用isNaN // 因为isNaN(undefined) 为true return (a === b) || (a !== a && b !== b) }
- 判断引用类型的两者是否一致
代码未做优化,可能有点💩💩(完全按照上述思路编写),有优化空间
function isEqual(a, b) { if (!isSameType(a, b)) { return false } // 都是数组 if (Array.isArray(a)) { if (a.length !== b.length) { return false } // 逐项判断 for (let i = 0; i < a.length; i++) { let _a = a[i] let _b = b[i] // 类型不等 if (!isSameType(_a, _b)) { return false } // 值类型,值不等 if (isValueType(_a) && !isSame(_a, _b)) { return false } // 对象 - 递归判断了 if(isObject(_a)&&!isEqual(_a,_b)){ return false } } } else { // 都是普通对象 let aKeys = Reflect.ownKeys(a) let bKeys = Reflect.ownKeys(b) // 键数量不一致 if(aKeys.length!==bKeys.length){ return false } for (const aKey of aKeys) { let _a = a[aKey] let _b = b[aKey] // 类型不等 if (!isSameType(_a, _b)) { return false } // 值类型,值不等 if (isValueType(_a) && !isSame(_a, _b)) { return false } // 对象 - 递归判断了 if(isObject(_a)&&!isEqual(_a,_b)){ return false } } } return true }
准备工作到此算完成了,现在咱们开始组装零件
去重函数实现
function duplicate(arr) { // 存放最终结果 const res = [] // 遍历咱们的原数组 for (const a of arr) { // 判断是否存在 let isExist = res.findIndex(b => { // 这里就变成了当前项与原来的每一项进行比较 if (!isSameType(a, b)) { return false } if (isValueType(a) && !isSame(a, b)) { return false } if (isObject(a) && !isEqual(a, b)) { return false } return true }) !== -1 // 不存在则放入 if (!isExist) { res.push(a) } } return res }
最终测试
测试数据
let obj1 = { age: [1, 2, { data: [1, 2, 3], name: 'a' }], name: 'xm' } let obj2 = { name: 'xm', age: [1, 2, { name: 'a', data: [1, 2, 3] }] } let a = [1, 2, '1', '2', 1, null, null, undefined, undefined, {}, {}, [], [], [1], [1], [1, 2], [2, 1], ['1'], ['1'], obj1, obj2, false, false, NaN,NaN, true, true]
结果
duplicate(a) [ 1, 2, '1', '2', null, undefined, {}, [], [ 1 ], [ 1, 2 ], [ 2, 1 ], [ '1' ], { age: [ 1, 2, { data: [1, 2, 3], name: 'a' } ], name: 'xm' }, false, NaN, true ]
总结
- 考查知识点
- 类型判断
- 值类型特点
- 引用类型特点
- NaN的特点
- 整个从代码量看下来,就是判断两个对象是否值相等那里稍微复杂一点
- 做笔试题只要把边界情况考虑清楚,理清思路,在不考虑时间复杂度的时候,用朴素的代码就能完成