
复杂的数组去重
让我们先看一个简单的数组:
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
数组中
const findDuplicates = (source) => {
const keys = Object.keys(source[0]);
let unique = [],
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: unique, duplicates: 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
方法的修改版本如下:
const 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"}
本文作者 Milos Protic,转载请注明来源链接:
原文链接:https://devinduct.com/blogpost/17/handling-array-duplicates-can-be-tricky
本文链接:https://tie.pub/2019/06/handling-array-duplicates-can-be-tricky/