从数组中删除重复项

106

我有一个对象数组,看起来像这样:

var array = [
    {id:123, value:"value1", name:"Name1"},
    {id:124, value:"value2", name:"Name1"},
    {id:125, value:"value3", name:"Name2"},
    {id:126, value:"value4", name:"Name2"}
    ...
];

正如您所看到的,一些名称是重复的。我想获得一个仅包含名称的新数组,但如果某个名称重复,我不想再添加它。我想要这个数组:

如上所述,有些名称是重复的。我想获取一个仅含名称的新数组,但如果某个名称重复,则不希望再次添加它。我需要这个数组:

var newArray = ["Name1", "Name2"];

我试图使用 map 来实现这个:

var newArray = array.map((a) => {
    return a.name;
});

但问题在于这会返回:

newArray = ["Name1", "Name1", "Name2", "Name2"];

如何在map中设置某些条件,以便它不会返回已存在的元素? 我想使用map或其他ECMAScript 5或ECMAScript 6功能来实现这一点。


3
从数组中删除重复项。 - Tushar
6
可以考虑使用 Set 吗?https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set - ppasler
1
可能是JavaScript数组中删除重复项的重复问题。 - Bergi
为什么包含id:125的那一行没有以逗号结尾? - Peter Mortensen
请注意,如果数组很大,所有使用.indexOf()的答案都会具有较差的性能,因为它们具有二次时间复杂度。我建议使用ES6 Set或将ES5对象用作字典。 - joeytwiddle
17个回答

220

使用ES6,您可以在仅映射对象名称后,使用Set来获取唯一值。

此提案使用扩展语法...来收集新数组中的项目。

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }],
      names = [...new Set(array.map(a => a.name))];

console.log(names);


10
了解一下,... 是什么作用? - Rajshekar Reddy
10
@RajshekarReddy,这是一个使用展开语法 ... 来构建可迭代对象数组的方法。请参考 MDN文档 - Nina Scholz
6
我不是因为"Set"本身而点赞,而是因为巧妙地使用了展开运算符。学到新东西或看到新东西应用到实际例子中总是很不错的,所以这篇文章值得点赞。 - briosheje
36
这只是我的个人观点,但是它是相当普遍的。使用 Array.from 可以更好地表达转换的意图(对于新手来说也更易理解/搜索),而展开语法只有在要展开的元素有多个时才应该使用。也许称其为“不良做法”有点过于严厉了,抱歉,我只是希望阻止它成为常见习惯。 - Bergi
4
@KRyan 是在问ES6是不是新的版本。它已于2015年6月被定稿。现在是开始了解和使用新功能的时候了。 - edc65
显示剩余7条评论

66

如果您正在寻找一个不包含ES 6(没有Set)的JavaScript解决方案,您可以使用Array的reduce方法

var array=[
  {id:123, value:"value1", name:"Name1"},
  {id:124, value:"value2", name:"Name1"},
  {id:125, value:"value3", name:"Name2"},
  {id:126, value:"value4", name:"Name2"}
];
var names = array.reduce(function (a, b) {
  if (a.indexOf(b.name) == -1) {
    a.push(b.name)
  }
  return a;
}, []);

console.log(names);


2
这个答案的好处在于它不需要中间数组或集合,特别是那些仅包含名称本身的数组或集合。(它直接从对象数组转换为对象数组。)+1 - jpmc26
@Iwrestledabearonce,这第二个参数和返回的对象不是同一个吗? - Kaiido
筛选器不会作用于原始数组,它会创建一个新的数组。而 reduce 则不同:它输出你传入的内容。 - Kaiido
1
@Dekel - 更简洁的版本 :) var names = array.reduce(function(a, b) { a.indexOf(b.name) === -1 && a.push(b.name) return a; }, []); - Rayon
3
@Rayon 请不要过于关注源代码的大小,这是许多自学程序员常见的陷阱,最重要的是,你的程序应该高效并且易读。 - user1636522
显示剩余2条评论

18

个人而言,我不明白为什么每个人都要使用ES6。如果是我的代码,我更希望支持尽可能多的浏览器。

var array=[
{id:123, value:"value1", name:"Name1"},
{id:124, value:"value2", name:"Name1"},
{id:125, value:"value3", name:"Name2"},
{id:126, value:"value4", name:"Name2"}
];

   // Create array of unique names
var a = (function(a){
  for (var i = array.length; i--;)
    if (a.indexOf(array[i].name) < 0) a.push(array[i].name);
  return a;
})([]);

console.log(a);


2
使用对象作为字典而不是一直使用 indexOf 查找,这样会更高效。例如,使用 added[name] = trueif(added[name]) 而不是 if(a.indexOf(name)) - Bojidar Marinov
8
个人认为,为什么每个人都要在ES6上大费周折,为什么要冒着“goto fail”和变量泄漏的风险呢?ES6被纳入标准是有很好的原因的,而且大多数情况下都可以轻松地通过polyfill/转换来支持旧版浏览器。 - Jared Smith
5
更易读?这只是一个将项目推入数组的 for 循环。再没有比这更易读的了...... - I wrestled a bear once.
5
说实话,这只是感觉你在努力找一些事情来抱怨。 - I wrestled a bear once.
3
我指的是著名的苹果SSL漏洞。但它并不那么有名,你不能仅仅在随意评论中提及它,而期望其他人能够认出它。 - Hejazzman
显示剩余7条评论

17

你也可以简单地将 mapfilter 结合使用。

var array = [
  {id:123, value:"value1", name:"Name1"},
  {id:124, value:"value2", name:"Name1"},
  {id:125, value:"value3", name:"Name2"},
  {id:126, value:"value4", name:"Name2"}
];

var unique = array
  .map( item => item.name )
  .filter( ( item, idx, arr ) => arr.indexOf( item ) == idx ) 

console.log(unique)


13
您可以使用Object.keys()获取给定对象的自身可枚举属性名称数组,该对象是通过使用Array.prototype.reduce()迭代array变量得到的结果,其中键是被解构的名称destructed
代码:

const array = [{id:123, value:"value1", name:"Name1"}, {id:124, value:"value2", name:"Name1"}, {id:125, value:"value3", name:"Name2"}, {id:126, value:"value4", name:"Name2"}],
      names = Object.keys(
        array.reduce((a, { name }) => (a[name] = 1, a), {})
      )

console.log(names)


9

这里有很多好的答案。我想提供一些多样性,希望能为您提供另一种视角。

在JavaScript中,数组属于对象类型,因此它们可以同时用作哈希表。通过使用这个功能,我们可以在单个reduce操作中大大简化要完成的任务,时间复杂度为O(n)。

如果您不喜欢数组包含除数组键以外的某些属性,您可以考虑同时保留一个单独的哈希表对象。

var array = [{id:123, value:"value1", name:"Name1"},
             {id:124, value:"value2", name:"Name1"},
             {id:125, value:"value3", name:"Name2"},
             {id:126, value:"value4", name:"Name2"}
            ],
result = array.reduce((p,c) => p[c.name] ? p : (p[c.name] = true, p.push(c.name), p), []);
console.log(result);


这个ES5解决方案性能良好,但是使用数组来完成两个目的很容易混淆,并且如果其中一个名称只是一个数字,则存在危险。我建议在单独的对象o上执行p[c.name]操作,然后在之后将其丢弃。 - joeytwiddle

8

我认为如果你只需要name值,那么使用Set是最好的选择。

然而,如果你想根据name属性获取一个唯一对象的数组,我建议使用Map。创建Map的一种快速方法是使用一个[key, value]数组的列表:

const array = [{ id: 123, value: "value1", name:"Name1" }, { id: 124, value: "value2", name: "Name1" }, { id: 125, value: "value3", name: "Name2" }, { id: 126, value: "value4", name: "Name2" }],
      unique = new Map(array.map(obj => [obj.name, obj]));

// To get the unique objects
const uniques = Array.from(unique.values());

// Get the names like you already did:
console.log("Names:", uniques.map(obj => obj.name));

// If you ever need the complete array of unique objects, you got a ref:
console.log(JSON.stringify(uniques));
.as-console-wrapper { min-height: 100%; }

< p> Map 的附加好处是,您可以获得 filter 功能,该功能会削减非唯一项,而不会失去与源对象的连接。当然,只有在需要多次引用唯一对象集时才需要它。


4
使用ES6,这个应该可以完成任务。

var array=[
    {id:123, value:"value1", name:"Name1"},
    {id:124, value:"value2", name:"Name1"},
    {id:125, value:"value3", name:"Name2"},
    {id:126, value:"value4", name:"Name2"}
];

var set = new Set();

array.forEach((a)=>{
    set.add(a.name);
}); 

console.log(Array.from(set));


11
map 应该与纯函数一起使用,而不是具有副作用。这应该由 forEachreduce 来完成。 - Vincent van der Weele

4
如果你只能使用ES5,我建议使用Lodash的_.uniq
var newArray = _.uniq(array.map(function(a) {
  return a.name;
}));

2
使用UnderscoreJS:

array = [{id:123, value:"value1", name:"Name1"}, {id:124, value:"value2", name:"Name1"}, {id:125, value:"value3", name:"Name2"}, {id:126, value:"value4", name:"Name2"}];
get_names =  _.pluck(_.uniq(array, 'name'), 'name')
console.log(get_names)
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

`


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接