从数组中删除重复项


100

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

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功能来执行此操作。


2
然后从阵列中删除重复项。
Tushar



为什么包含id:125的行不以逗号结尾?
彼得·莫滕森

请注意,如果数组很大,则使用.indexOf()的所有答案的性能都会很差,这是因为它们的二次时间复杂度。我建议使用ES6 Set或将ES5对象用作字典。
joeytwiddle

Answers:


210

使用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);


9
对于我的信息..怎么...办?
Rajshekar Reddy

9
@RajshekarReddy,这是用于使用可迭代对象构建数组的扩展语法...
Nina Scholz

5
我不是为“集合”本身而投票,而是为了对散布运算符的巧妙使用。学习新知识或将新知识应用到实际示例中总是很高兴的,因此,这篇文章值得推荐。
briosheje

33
这只是我个人的看法,但很普遍。使用可以Array.from更好地传达转换的意图(并且也更易于理解/搜索新手),仅当散布元素是众多元素之一时,才应使用散布语法。也许称它为“坏习惯”有点太苛刻,对不起,我只是希望防止它成为常见的成语。
Bergi

3
@KRyan ES6这么新吗?它已于2015年6月完成。是时候开始理解和使用新功能了
edc65 '16

64

如果您正在寻找不是ES 6(未设置)的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 2016年

@Iwrestledabearonce,第二个参数与返回的参数不是同一对象吗?
Kaiido

是的,但是它并不能像排序或过滤器那样直接从一个地方移到另一个地方,而是从一个空数组重建而来。
我曾经摔过一只熊。

1
@Dekel —较小的版本:)var names = array.reduce(function(a, b) { a.indexOf(b.name) === -1 && a.push(b.name) return a; }, []);
人造丝

3
@Rayon请不要过于注意源代码的大小,这对于许多自制的程序员来说是一个常见的陷阱,最重要的是,您的程序应该高效且易读。

16

就我个人而言,我不明白为什么每个人都喜欢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-ing 会更高效吗?例如,用added[name] = trueif(added[name])代替if(a.indexOf(name))
Bojidar Marinov

7
“我个人不明白为什么每个人都对es6寄予厚望”为什么要求爱a goto fail,将索引变量泄漏到封闭范围内,等等。等等。ES6被编入标准是有充分理由的,这是最部分对于旧版浏览器来说很容易实现polyfill / transform。
贾里德·史密斯

5
更具可读性?它是一个怪异的for循环,它将项目推入数组。没有比这更具可读性了……
我摔跤过一次熊。

4
坦白地说,这就像您在尝试寻找难以抱怨的事情那样太努力了。
我曾经摔过一只熊。

2
“我指的是著名的Apple SSL错误” —可以随意在上下文之外的随机注释中引用它,并期望人们认识到它,这一点并不那么出名。
Hejazzman,

14

您也可以简单地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)


11

您可以使用Object.keys()array具有Array.prototype.reduce()的迭代变量的对象结果中获取给定对象自己的可枚举属性名称的数组,其中键是被破坏的名称

码:

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)


7

这里有很多好的答案。我只是想做出一些贡献,希望能给您另一个视角。

数组是JavaScript中的对象类型,因此它们可以同时用作哈希。通过使用此功能,我们可以在一次简化操作中以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解决方案具有良好的性能,但是将数组用于两个目的会造成混淆,并且如果名称之一仅仅是数字,则很危险。我建议p[c.name]在一个单独的对象上进行处理o,然后再将其丢弃。
joeytwiddle

7

我同意,如果您只需要这些name值,Set那么走是要走的路。

但是,如果您想基于该name属性获取一组唯一的对象,则建议使用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%; }

的另一个好处Map是,您既获得了filter消除非唯一性的功能,又没有失去与源对象的连接。当然,仅当您需要多次引用唯一的对象集时才需要。



2

使用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应该与纯功能一起使用,没有副作用。这将是forEach或的工作reduce
Vincent van der Weele 2016年

1

使用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>

`


0

在ES5中,将对象用作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"}
];

var allNames = array.map(item => item.name);

var map = {};
allNames.forEach(name => {
  map[name] = true;
});
var uniqueNames = Object.keys(map);

console.log(uniqueNames);

如果愿意,可以在一个表达式中执行相同的操作:

var uniqueNames = Object.keys(allNames.reduce((m, n) => (m[n] = true, m), {}));

但是我发现命令式更易于阅读。


为了清楚起见,我使用了ES6箭头功能。如果您确实是针对没有转译器的ES5,那么您将需要使用完整的表单:function (item) { return item.name; }
joeytwiddle

作为替代Object.keys()此答案用于filter()仅保留那些尚未存储在地图中的名称,这非常好用。
joeytwiddle


0

如果想要最大程度的兼容性,请使用array#forEach()和这样的array#indexOf()方法,简洁的语法:

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

// initialize an empty array named names
let names = [];

// iterate through every element of `array` & check if it's 'name' key's value already in names array if not ADD it 
array.forEach(function(element) { if (names.indexOf(element.name) === -1) names.push(element.name) });
// or use tilde like this:
//array.forEach(function(element) { if (~names.indexOf(element.name)) names.push(element.name) });

console.log(names);

但是,如果兼容性不是问题,请使用ECMAScript 6Set对象,array#map以及Array.from()类似的方法:

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" }];

// iterate through every element from array using map and store it in Set(a Set won't have duplicates) then convert the Set back to Array(using Array.from)
let names = Array.from(new Set(array.map(element => element.name)));

console.log(names);


0

这就是我使用单独的空数组的方式。

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 array2 = []		
		
for (i=0; i<array.length;i++){						
   if (array2.indexOf(array[i].name) == -1){				
     array2.push(array[i].name);
    }
}			

console.log(array2)	


-1

对于那些寻求1班轮的人

const names = array.reduce((acc, {name}) => acc.includes(name) ? acc : [name, ...acc], []);

或不使用数组原型上的方法

const { reduce, includes } = Array;
const names = reduce(array, (acc, {name}) => includes(acc, name) ? acc : [name, ...acc], []);

可能对于编写一些用于处理此问题的纯函数很有用

const get_uniq_values = (key, arr) => reduce(arr, (a, o) => includes(a, o[key]) ? a : [o[key], ...a], []);

-1
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"}];

function __checkArray(__obj){
    var flag = true;
    for(let i=0; i < __array.length; i++){
        if(__obj.id == __array.id){
            flag = false;
            break;
        }
    }

    return flag;
}

var __valToPush = {id: 127, value: "value5", name: "Name3"};
if(__checkArray(__valToPush)){
    __array.push(__valToPush)
}

11
您是否有特别的理由要使用这么多的下划线?
妮娜·斯科尔斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.