数组(包含对象等类型元素)去重
解决思路
一个数组(包含对象等类型元素)去重函数,需要在基础类型判断相等条件下满足以下条件:
- 如果元素是数组类型,则需要数组中的每一项相等
- 如果元素是对象类型,则需要对象中的每个键值对相等
去重本身就是遍历数组,然后比较数组中的每一项是否相等而已,所以关键步骤有两步:比较、去重
比较:
- 首先判断类型是否一致,类型不一致则返回认为两个数组元素是不同的,否则继续
- 如果是数组类型,则递归比较数组中的每个元素是否相等
- 如果是对象类型,则递归比较对象中的每个键值对是否相等
- 否则,直接 === 比较
去重:
- 采用
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}]
不产生新数组,删除数组里的重复元素
即在原有的数组上进行去重操作
排序去重
sort() 方法
用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的
const removeDuplicates = (nums) => {
// 原地排序
nums.sort()
// 去重
let len = 1
for (let i = 1; i < nums.length; i++)
if (nums[i] != nums[i-1]) nums[len++] = nums[i];
// 删除重复项
nums.splice(len)
return nums
}
// 测试
removeDuplicates([1, 2, 3, 1, 3])
// [1, 2, 3]
优化
const removeDuplicates = (nums) => {
let len = nums.length - 1
for(let i = len; i>=0; i--) {
if(nums.indexOf(nums[i]) != i) {
nums[i] = nums[len --]
}
}
// 删除重复项
nums.splice(len+1)
return nums
}
// 测试
removeDuplicates([1, 2, 3, 1, 3])
// [1, 2, 3]
产生一个新数组,进行去重
Set(ES6中的方法)
function remove(arr) {
return Array.from(new Set(arr))
}
// 或者
var remove = arr => [...new Set(arr)]
// 测试
var arr = [1, 2, 2, 3]
remove(arr); // [1, 2, 3]
reduce
reduce() 方法
接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce() 可以作为一个高阶函数,用于函数的 compose。
function remove (arr) {
return arr.sort().reduce((acc, cur) => {
if (acc.length === 0 || acc[acc.length - 1] !== cur) {
acc.push(cur);
}
return acc
}, [])}
;
// 测试
var arr = [1, 2, 2, 3]
remove(arr); // [1, 2, 3]
filter
filter() 方法
创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
function remove(arr) {
return arr.filter( (element, index, array) => {
return array.indexOf(element) === index
})
}
// 测试
var arr = [1, 2, 2, 3]
remove(arr); // [1, 2, 3]