•
阅读 4 分钟
复杂的数组去重
让我们先看一个简单的数组:
const cars = ['Mazda', 'Ford', 'Renault', 'Opel', 'Mazda']
你可以看到,第一项和最后一项是相同的。在只有原始类型的数组中找到重复项是简单的。为了目的,我们可以简单使用 filter
方法和提供 indexof
回调函数。
const unique = cars.filter((car, idx) => cars.indexOf(car) === idx)
console.log(unique) // ['Mazda', 'Ford', 'Renault', 'Opel']
方法 indexOf
会找寻数组中的首先满足的项。这是为什么我们对比 indexOf
返回的索引确认是否是重复的项。
ES6 发布后,我们还可以使用 Set
配合扩展运算符或者 Array.from
来数组去重:
const cars = ['Mazda', 'Ford', 'Renault', 'Opel', 'Mazda']
const uniqueWithSpreadOperator = [...new Set(cars)]
console.log(uniqueWithSpreadOperator) // outputs ["Mazda", "Ford", "Renault", "Opel"]
const uniqueWithArrayFrom = Array.from(new Set(cars))
console.log(uniqueWithArrayFrom) // outputs ["Mazda", "Ford", "Renault", "Opel"]
找寻重复对象
这是难点项。通过引用而不是值或结构来比较对象。这意味着如果我们比较两个完全相同的对象,它们将不匹配。我们不能简单地通过 obj1 === obj2
来比较它们。
const obj1 = {
name: 'John',
surname: 'Doe',
}
const obj2 = {
name: 'John',
surname: 'Doe',
}
const match = obj1 === obj2
console.log(match) // false
现在,如果处理一个含有对象重复项的数组,我们怎么过滤它们呢?考虑我们刚刚读的内容,不可能简单地使用 indexOf
。
数组例子:
const names = [
{
name: 'John',
surname: 'Doe',
},
{
name: 'Muhamed',
surname: 'Ali',
},
{
name: 'Mike',
surname: 'Tyson',
},
{
name: 'John',
surname: 'Doe',
},
{
name: 'John',
surname: 'Doe',
},
{
name: 'Mike',
surname: 'Tyson',
},
{
name: 'Mike',
surname: 'Tyson',
},
]
如你所见,我们有些重复项,让我们提供发现重复项的方法。
一个长版本
在这种方法中,我们将手动循环遍历源数组(方法 forEach
),并使用该 find
方法检查结果数组中是否存在每个项。考虑到我们有一个对象数组,我们必须比较当前对象的每个属性,以确保项目是相同的。细分为步骤如下所示:
- 获取对象属性
- 定义结果数组(
unique
和duplicates
) - 循环遍历源数组
- 尝试在
unique
数组中找到当前项 - 如果找到该项,请将其推
duplicates
入unique
数组中
function findDuplicates(source) {
const keys = Object.keys(source[0])
const unique = []
const duplicates = []
source.forEach((item, idx) => {
if (idx == 0) {
unique.push(item)
return
}
const resultItem = unique.find((resultItem) => {
let notFound = true
keys.forEach((key) => {
notFound = notFound && item[key] != resultItem[key]
})
return !notFound
})
;(!resultItem ? unique : duplicates).push(item)
})
return { unique, duplicates }
}
const result = findDuplicates(names)
console.log(result.unique, result.duplicates)
// unique items
// 0: {name: "John", surname: "Doe"}
// 1: {name: "Muhamed", surname: "Ali"}
// 2: {name: "Mike", surname: "Tyson"}
// duplicate items
// 0: {name: "John", surname: "Doe"}
// 1: {name: "John", surname: "Doe"}
// 2: {name: "Mike", surname: "Tyson"}
// 3: {name: "Mike", surname: "Tyson"}
一个短版本
我们可以使用 reduce
方法实现相同的事情。这是一个非常有用的方法,来转换数组到想要的结果。它接收一个回调参数来处理数组的每一项。方法回调函数的返回值是每一个迭代的累加值。关于 reduce
的更多介绍请看 MDN 文档。
👌,回到我们的代码,findDuplicates
方法的修改版本如下:
function findDuplicates(source) {
const keys = Object.keys(source[0])
return source.reduce(
(acc, item) => {
const resultItem = acc.unique.find((x) => {
let notFound = true
keys.forEach((key) => {
notFound = notFound && item[key] != x[key]
})
return !notFound
})
;(!resultItem ? acc.unique : acc.duplicates).push(item)
return acc
},
{
unique: [],
duplicates: [],
}
)
}
该修改后的版本应当返回与之前相同的数组。
// unique items
// 0: {name: "John", surname: "Doe"}
// 1: {name: "Muhamed", surname: "Ali"}
// 2: {name: "Mike", surname: "Tyson"}
// duplicate items
// 0: {name: "John", surname: "Doe"}
// 1: {name: "John", surname: "Doe"}
// 2: {name: "Mike", surname: "Tyson"}
// 3: {name: "Mike", surname: "Tyson"}
文章由 吳文俊 翻译,原文地址 Handling Array Duplicates Can Be Tricky,转载请注明来源。
标签: javascript es6 array