处理数组重复可能会比较棘手 进一步阅读

2025-06-07

处理数组重复可能会很棘手

进一步阅读

原帖发布于我的博客。查看更多最新内容

让我们首先定义一个简单的数组:

const cars = [
    'Mazda', 
    'Ford', 
    'Renault', 
    'Opel', 
    'Mazda'
]

如您所见,第一个元素和最后一个元素是相同的。考虑到我们有一个原始类型的数组,查找这个重复元素非常简单。为了实现这一点,我们可以简单地在提供的回调函数中filter结合使用 和indexOf

const unique = cars.filter((car, idx) => cars.indexOf(car) === idx);
console.log(unique); // outputs ['Mazda', 'Ford', 'Renault', 'Opel']

请注意,该indexOf方法将返回数组中某个元素的第一次出现。因此,我们可以indexOf在每次迭代中将该方法返回的索引与当前索引进行比较,以判断当前元素是否重复。

查找对象重复项

这部分比较棘手。对象是通过引用而不是值或结构体进行比较的。这意味着,如果我们比较两个完全相同的对象,它们将不会匹配。我们不能仅仅obj1 === obj2因为它们的比较方式而做出类似的操作。

const obj1 = {
   name: 'John',
   surname: 'Doe'
}

const obj2 = {
   name: 'John',
   surname: 'Doe'
}

const match = obj1 === obj2;
console.log(match) // outputs 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方法检查每个项目是否存在于结果数组中。
考虑到我们有一个对象数组,我们必须比较当前对象的每个属性,以确保项目相同。该过程分解为几个步骤,如下所示:

  1. 获取对象属性
  2. 定义结果数组(uniqueduplicates
  3. 循环遍历源数组
  4. unique尝试在数组中找到当前项
  5. 如果找到该项目,则将其推入数组,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);

// expected output

// 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"}

就这样吧。感谢您的阅读,我们下篇文章再见。

进一步阅读

请参阅此备忘单,它将指导您了解数组操作时最常见的用例。

文章来源:https://dev.to/proticm/handling-array-duplicates-can-be-tricky-4ob0
PREV
Tips and Tricks for Better JavaScript Conditionals and Match Criteria
NEXT
CSS 变量速查表简介声明示例用例继承默认(后备)值无效值动态使用(JavaScript)后续内容进一步阅读