简介
在消费API或从数组中检索数据时,你可能会遇到重复的数据,但你只想选择唯一的值。
例如,假设你有一个代表餐厅菜单的数组,如下图所示。你可能需要从该数组中检索可用的类别--在这种情况下,你需要过滤该数组,只获得一次类别,避免重复。
const menu = [
{
name: "buttermilk pancakes",
category: "breakfast"
},
{
name: "diner double",
category: "lunch"
},
{
name: "godzilla milkshake",
category: "dinner"
},
{
name: "country delight",
category: "breakfast"
},
{
name: "egg attack",
category: "lunch"
}
];
在这篇文章中,我们将学习如何在JavaScript中去除数组中的重复部分,基本上是创建一个唯一值的数组。我们将看看几种不同的方法,这样你就能选择最适合你项目的方法。
最好的解决方案。使用*Set()*构造函数
Set是一个项目的集合,它是唯一的,也就是说,没有元素可以重复。假设我们有一个学校科目的数组,我们想从其中删除所有重复的内容。
let subjects = ["mathematics", "french", "english", "french", "mathematics"];
JavaScript为我们提供了一个创建集合的方便的构造函数--Set() 。它可以接受一个数组作为它的参数。在这种情况下,它创建一个集合,并从传递的数组中填充唯一的值。这种方法的问题在于,Set() 构造函数创建的是一个集合,而不是一个数组(我们需要的)。
我们将绕过这个不受欢迎的行为,使用 传播操作符- 也被称为三点运算符(...)。当与一个集合(用Set() 构造函数产生)结合时,传播操作符可以将集合的值传播到一个数组中。
let uniqueSubjects = [...new Set(subjects)];
console.log(uniqueSubjects); // ["mathematics", "french", "english"]
我们也可以对一个对象的数组这样做,让我们在 menu 数组的例子上说明一下。
let categories = [...new Set( menu.map((menu) => menu.category))];
console.log(categories); // ["breakfast","lunch","dinner"]
**注意:**使用Set() 构造函数从一个数组中删除重复的元素需要线性时间--O(n) (n 是原数组中的元素数)。所有其他去除重复的方法都需要O(n²) 。因此,我们强烈建议你在任何可能的情况下使用Set() 构造函数。
使用*filter()和indexOf()*方法
filter() 方法用于循环浏览一个数组,并返回一个仅由符合给定条件的元素组成的新数组,而indexOf() 方法用于返回该元素在数组中首次出现的索引。
例如,假设我们有一个主题数组,我们想检查每个元素的索引。
let subjects = ["mathematics", "french", "english", "french", "mathematics"];
let subjectsIndex = [];
subjects.map((subject) => {
subjectsIndex.push(subjects.indexOf(subject));
});
console.log(subjectsIndex); // [0,1,2,1,0]
因为我们知道过滤器方法根据给定的标准返回一个新的数组,我们现在可以用它来只包括那些索引与它们的indexOf() 。
let subjects = ["mathematics", "french", "english", "french", "mathematics"];
let uniqueSubjects = subjects.filter((subject, index) => {
return subjects.indexOf(subject) === index;
});
console.log(uniqueSubjects); // ["mathematics","french","english"]
假设我们想利用menu 数组,我们首先要循环浏览该数组,将类别存储在一个新的数组中,然后现在利用filter() 和indexOf() 方法。
let categoriesArray = menu.map((menu)=>menu.category)
let uniqueCategories = categoriesArray.filter((category, index) => {
return categoriesArray.indexOf(category) === index;
});
console.log(uniqueCategories); // ["breakfast","lunch","dinner"]
如果你想阅读更多关于
filter()方法的信息--请阅读我们的《JavaScript的filter()方法指南》!
使用*reduce()和includes()*方法
Reduce总是有点难以掌握,但却能执行一个非常强大的减少操作,这是从函数式编程中借用的。reduce() 方法用于减少数组的元素,并根据你传入的一些减少函数将它们组合成一个最终的数组,而includes() 方法则是在一个元素在数组中时返回真,否则返回假。
下面的例子是对一个数组的元素进行迭代,并检查某个特定的元素是否在结果中,否则我们就把它加上。
let subjects = ["mathematics", "french", "english", "french", "mathematics"];
let uniqueSubjects = subjects.reduce((result, subject) => {
return result.includes(subject) ? result : [...result, subject];
}, []);
console.log(uniqueSubjects); // ["mathematics","french","english"]
假设我们想利用菜单数组,我们首先要在数组中进行循环,将类别存储在数组中,然后现在利用前面解释的reduce() 和includes() 方法。
let categoriesArray = menu.map((menu)=>menu.category)
let uniqueCategories = categoriesArray.reduce((result, category) => {
return result.includes(category) ? result : [...result, category];
}, []);
console.log(uniqueCategories); // ["breakfast","lunch","dinner"]
**注意:**注意我们在这一节中也使用了传播操作符。
使用*forEach()和includes()*方法
这与使用filter() 和includes() 方法的效果差不多。我们只是使用forEach() 方法来遍历数组,然后只向一个新的数组添加尚未存在的元素。
let subjects = ["mathematics", "french", "english", "french", "mathematics"];
let uniqueSubjects = [];
subjects.forEach((subject) => {
if (!uniqueSubjects.includes(subject)) {
uniqueSubjects.push(subject);
}
});
console.log(uniqueSubjects); // ["mathematics","french","english"]
假设我们想利用menu 数组,我们首先要在数组中进行循环,将类别存储在一个数组中,然后再利用前面解释的forEach() 和includes() 方法。
let categoriesArray = menu.map((menu)=>menu.category)
let uniqueCategories = [];
categoriesArray.forEach((category) => {
if (!uniqueCategories.includes(category)) {
uniqueCategories.push(category);
}
});
console.log(uniqueCategories); // ["breakfast","lunch","dinner"]
结论
在这篇文章中,我们已经看到了四种不同的方法来移除数组中的重复内容。在ES6中引入的Set() ,使之变得更加简单和高效。使用Set() 构造函数从数组中移除重复的元素需要线性时间--O(n) (n 是原数组中元素的数量)。所有其他去除重复的方法都需要O(n²) 。因此,我们强烈建议你在任何可能的情况下使用Set() 构造函数。