我有一个包含对象数组的对象。

things = new Object();

things.thing = new Array();

things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});


我想知道从数组中删除重复对象的最佳方法是什么。举例来说,things.thing会变成...

{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}


评论

您是说如何停止将所有相同参数添加到数组的哈希表/对象?

Mathew->如果更简单的方法是首先防止将重复的对象添加到数组中,而不是稍后将其过滤掉,是的,这也很好。

人们如何命名变量,这让我感到惊讶。有时我认为他们真的想使它不必要地复杂。接下来将看到aaaaa.aaaa.push(...):)

对事物进行了精简。这不必要地使问题和答案变得复杂。

#1 楼

一种原始方法是:

var obj = {};

for ( var i=0, len=things.thing.length; i < len; i++ )
    obj[things.thing[i]['place']] = things.thing[i];

things.thing = new Array();
for ( var key in obj )
    things.thing.push(obj[key]);


评论


永远不要在for循环中使用该长度,因为它会减慢每次迭代时计算它的速度。将其分配给循环外的变量,然后传递该变量而不是Things.thing.length。

– 0v3rth3d4wn
2014年8月26日12:56



@aefxx我不太了解此功能,如何处理“位置”相同但名称不同的情况,应该考虑还是不考虑dup?

–关
15年6月23日在21:48

尽管这可行,但它不会处理排序的数组,因为从不保证顺序获取键。因此,您最终再次对其进行了排序。现在,假设未对数组进行排序,但是其顺序很重要,则无法确保顺序保持不变

– Deepak G M
19年4月17日在6:31

@DeepakGM你是完全正确的。答案将不会(必须)保留给定的顺序。如果这是一项要求,则应寻找另一种解决方案。

–aefxx
19年4月17日在17:03

我如何修改上面的内容以从包含X以及重复数据删除的数组中删除对象?

–瑞安·霍尔顿(Ryan Holton)
2月9日下午12:36

#2 楼

Q4312079q的魔法怎么样?

things.thing = things.thing.filter((thing, index, self) =>
  index === self.findIndex((t) => (
    t.place === thing.place && t.name === thing.name
  ))
)


参考URL

更通用的解决方案是:

< pre class =“ lang-js prettyprint-override”> es6

Stackblitz示例

评论


可以简化为:Things.thing = Things.thing.filter((thing,index,self)=> self.findIndex(t => t.place === something.place && t.name === Things。名称)===索引)

–乔什·科尔(Josh Cole)
17年3月13日在12:12

完美的作品! var uniqueArrayOfObjects = arrayOfObjects.filter(function(obj,index,self){return index === self.findIndex(function(t){return t ['obj-property'] === obj ['obj-property'] });});确保使用正确的JS语法。

– Mohamed Salem Lamiri
17年11月14日在12:55

@vsync只是将@BKM的答案放在一起,一个通用的解决方案是:const uniqueArray = arrayOfObjects.filter((object,index)=> index === arrayOfObjects.findIndex(obj => JSON.stringify(obj)= == JSON.stringify(object))); jsfiddle.net/x9ku0p7L/28

–爱德里安
18年7月18日在11:33

此处的关键是findIndex()方法返回第一个元素的索引,因此,如果有第二个元素匹配,则将永远不会在过滤器中找到并添加它。我凝视了一下:)

– JBaczuk
19-09-13在23:21



在某些情况下,“更通用的解决方案”可能不起作用,因为JSON.stringify的顺序是不可预测的。此外,您没有缓存JSON.stringify(obj),因此它将被调用的次数比您预期的要多,这将对性能产生重大影响

–易卜拉欣·马尔里尔
19年11月6日,11:25



#3 楼

ES6 +的最短衬管

在数组中查找唯一的id

arr.filter((v,i,a)=>a.findIndex(t=>(t.id === v.id))===i)



多个属性具有唯一性(placename

arr.filter((v,i,a)=>a.findIndex(t=>(t.place === v.place && t.name===v.name))===i)


所有属性都是唯一的(对于大型数组,这将很慢)

arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i)


保持最后一次出现。

arr.slice().reverse().filter((v,i,a)=>a.findIndex(t=>(t.id === v.id))===i).reverse()


评论


v,i,a ==值,索引,数组

–詹姆斯B
10月9日18:23



findIndex在IE11中不起作用。收到错误“对象不支持属性或方法'findindex'”。

–甘纳什·萨纳普(Ganesh Sanap)
12月3日6:50

#4 楼

如果可以使用下划线或lodash之类的Javascript库,建议您查看它们的库中的_.uniq函数。从lodash开始:

_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])


基本上,您传入的数组是这里的对象常量,而您传入​​的属性是您想要删除原始对象中的重复项的数据数组,如下所示:

var data = [{'name': 'Amir', 'surname': 'Rahnama'}, {'name': 'Amir', 'surname': 'Stevens'}];
var non_duplidated_data = _.uniq(data, 'name'); 


更新:Lodash现在也引入了.uniqBy

评论


@Praveen Pds:我在代码示例中是否说过有关下划线的内容?我说过“ lodash”具有此功能,而下划线也具有类似的功能。在投反对票之前,请仔细阅读答案。

– ambodi
15年1月25日在11:08

//使用_underscore.js列出唯一的对象holdingObject = _.uniq(holdingObject,function(item,key,name){return item.name;});

– Praveenpds
15年1月26日在8:31

注意:您现在需要使用uniqBy而不是uniq,例如_.uniqBy(data,'name')...文档:lodash.com/docs#uniqBy

–drmrbrewer
17年6月14日在7:46

这里最简单的答案。

–卢卡斯·安德拉德(Lucas Andrade)
7月14日15:17

#5 楼

我有完全相同的要求,即根据单个字段上的重复项删除数组中的重复对象。我在这里找到了代码:Javascript:从对象数组中删除重复项

所以在我的示例中,我从数组中删除了具有重复licenseNum字符串值的任何对象。

var arrayWithDuplicates = [
    {"type":"LICENSE", "licenseNum": "12345", state:"NV"},
    {"type":"LICENSE", "licenseNum": "A7846", state:"CA"},
    {"type":"LICENSE", "licenseNum": "12345", state:"OR"},
    {"type":"LICENSE", "licenseNum": "10849", state:"CA"},
    {"type":"LICENSE", "licenseNum": "B7037", state:"WA"},
    {"type":"LICENSE", "licenseNum": "12345", state:"NM"}
];

function removeDuplicates(originalArray, prop) {
     var newArray = [];
     var lookupObject  = {};

     for(var i in originalArray) {
        lookupObject[originalArray[i][prop]] = originalArray[i];
     }

     for(i in lookupObject) {
         newArray.push(lookupObject[i]);
     }
      return newArray;
 }

var uniqueArray = removeDuplicates(arrayWithDuplicates, "licenseNum");
console.log("uniqueArray is: " + JSON.stringify(uniqueArray));


结果:

uniqueArray为:

[{"type":"LICENSE","licenseNum":"10849","state":"CA"},
{"type":"LICENSE","licenseNum":"12345","state":"NM"},
{"type":"LICENSE","licenseNum":"A7846","state":"CA"},
{"type":"LICENSE","licenseNum":"B7037","state":"WA"}]


评论


如果该函数也可以过滤“虚假”对象,则这将更为有用。 for(var i in array){if(array [i] [prop]){//有效的lookupObject [array [i] [prop]] = array [i]; } else {console.log('falsy object'); }}

–阿卜杜勒·萨迪克·雅尔辛(Abdul Sadik Yalcin)
17年11月6日在17:49

为什么不使用以下方法降低复杂度0(n):for(让i在originalArray中){if(lookupObject [originalArray [i] ['id']] ===未定义){newArray.push(originalArray [i]) ; } lookupObject [originalArray [i] ['id']] = originalArray [i]; }

– Tudor B.
19年2月17日在22:17



这是最好的方法,因为重要的是要知道您不想被复制的内容。现在可以通过用于E6标准的reducer来完成此操作吗?

–克里斯蒂安·马修(Christian Matthew)
19/09/16 '19:09

#6 楼

在一行中使用ES6 +,您可以通过键获取唯一的对象列表:

const unique = [...new Map(arr.map(item => [item[key], item])).values()]


它可以放入函数中:

function getUniqueListBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()]
}


这是一个有效的示例:




 const arr = [
    {place: "here",  name: "x", other: "other stuff1" },
    {place: "there", name: "x", other: "other stuff2" },
    {place: "here",  name: "y", other: "other stuff4" },
    {place: "here",  name: "z", other: "other stuff5" }
]

function getUniqueListBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()]
}

const arr1 = getUniqueListBy(arr, 'place')

console.log("Unique by place")
console.log(JSON.stringify(arr1))

console.log("\nUnique by name")
const arr2 = getUniqueListBy(arr, 'name')

console.log(JSON.stringify(arr2)) 





它是如何工作的

首先,重新映射数组,使其可以用作Map的输入。


arr.map(item => [item [key],item]);


,这意味着该数组的每个项目将被转换为另一个数组,其中2元素;所选键作为第一个元素,整个初始项作为第二个元素,这称为条目(例如数组条目,映射条目)。这是带有说明如何在Map构造函数中添加数组条目的示例的官方文档。

放置键时的示例:

[["here", {place: "here",  name: "x", other: "other stuff1" }], ...]


其次,我们将修改后的数组传递给Map构造函数,这就是神奇的事情。 Map将消除重复的键值,仅保留同一键的最后插入值。
注意:Map保持插入顺序。 (检查Map和对象之间的差异)


新Map(上面映射的条目数组)


第三,我们使用地图值检索原始项目,但是这次没有重复。


新Map(mappedArr).values()


最后一个是添加这些值放入一个新的新数组中,使其看起来像初始结构并返回:


返回[... new Map(mappedArr).values()]


评论


这不能回答原始问题,因为这是在搜索ID。问题需要整个对象在所有字段(例如,地点和名称)中都是唯一的

– L. Holanda
19/12/10在18:54

您的ES6函数看起来非常简洁实用。您能再解释一下吗?到底是怎么回事?是否删除了第一个或最后一个重复项?还是随机的,删除了哪些重复项?这将是有帮助的,谢谢。

–大卫·舒曼(David Schumann)
3月25日17:15

据我所知,将创建一个以属性值为键的Map。但是如何或是否保留数组的顺序并不是100%。

–大卫·舒曼(David Schumann)
3月25日18:16

@DavidSchumann,您好,我将更新答案并解释其工作原理。但是为了简短回答,保留了该顺序,删除了第一个顺序...考虑一下如何将其插入到地图中...它会检查键是否已存在,它将对其进行更新,然后保留最后一个

– V. Sambor
3月25日18:21



干净整洁的解决方案。太棒了

–莫汉
11月26日6:56



#7 楼

使用Set


的一根衬垫

 var things = new Object();

things.thing = new Array();

things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});

// assign things.thing to myData for brevity
var myData = things.thing;

things.thing = Array.from(new Set(myData.map(JSON.stringify))).map(JSON.parse);

console.log(things.thing) 





解释:




new Set(myData.map(JSON.stringify))使用字符串化myData元素创建Set对象。
Set对象将确保每个元素都是唯一的。
然后我使用Array.from根据创建的集合的元素创建一个数组。
最后,我使用JSON.parse将字符串化的元素转换回对象。


评论


问题是{a:1,b:2}将不等于{b:2,a:1}

– PirateApp
17年10月2日在10:00

请记住,日期属性会出现问题

– MarkosyanArtur
18 Mar 5 '18 at 6:31

该行使用原始对象数组中不存在的行对象创建随机空值。你能帮忙吗?

– B1K
18-10-16在16:26

#8 楼

ES6一个衬板在这里



 let arr = [
  {id:1,name:"sravan ganji"},
  {id:2,name:"pinky"},
  {id:4,name:"mammu"},
  {id:3,name:"sanju"},
  {id:3,name:"ram"},
];

console.log(Object.values(arr.reduce((acc,cur)=>Object.assign(acc,{[cur.id]:cur}),{}))) 




评论


如果您只想删除具有单个重复值的对象,而对于完全复制的对象则不是那么干净,那很干净。

–大卫·巴克(David Barker)
19年5月29日在23:56

@DavidBarker您是说一个对象有多个重复值?

–sravan ganji
9月1日20:08

是的,但是更具体地说,对象具有所有相同的值。

–大卫·巴克(David Barker)
9月2日7:42

#9 楼

如果只需要按对象的一个​​字段进行比较,则可以使用数组迭代方法执行此操作,这是另一种选择:

    function uniq(a, param){
        return a.filter(function(item, pos, array){
            return array.map(function(mapItem){ return mapItem[param]; }).indexOf(item[param]) === pos;
        })
    }

    uniq(things.thing, 'place');


评论


尽管它的阶数大于O(n²),但是这适合我的用例,因为我的数组大小始终小于30。谢谢!

–特雷克斯
16年7月13日在11:44

#10 楼

最简单的方法是使用filter




 var uniq = {}
var arr  = [{"id":"1"},{"id":"1"},{"id":"2"}]
var arrFiltered = arr.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
console.log('arrFiltered', arrFiltered) 




评论


在Stack Overflow上的良好做法是添加一个关于您的解决方案为何起作用的解释,尤其是您的解决方案比其他答案更好的地方。有关更多信息,请阅读如何回答。

–塞缪尔·刘(Samuel Liew)♦
18/09/15在5:14



这不能回答原始问题,因为这是在搜索ID。问题需要整个对象在所有字段(例如,地点和名称)中都是唯一的

– L. Holanda
19年12月10日在18:47

#11 楼

如果您要等到所有添加之后才能消除重复项,那么典型的方法是先对数组排序,然后消除重复项。排序避免了在遍历每个元素时使用N * N方式扫描每个元素的数组的方法。

“消除重复项”功能通常称为唯一或唯一。一些现有的实现可能将两个步骤结合在一起,例如,原型的uniq

如果您的库还没有一个,可以尝试一下(可以避免:-))。我个人认为这是最直接的方法:

    function unique(a){
        a.sort();
        for(var i = 1; i < a.length; ){
            if(a[i-1] == a[i]){
                a.splice(i, 1);
            } else {
                i++;
            }
        }
        return a;
    }  

    // Provide your own comparison
    function unique(a, compareFunc){
        a.sort( compareFunc );
        for(var i = 1; i < a.length; ){
            if( compareFunc(a[i-1], a[i]) === 0){
                a.splice(i, 1);
            } else {
                i++;
            }
        }
        return a;
    }


评论


如果没有自然的排序顺序,这将不适用于通用对象。

– Tim Down
2010-2-8在9:28

没错,我添加了用户提供的比较版本。

–maccullt
2010-2-8在10:57

用户提供的比较版本将无法使用,因为如果您的比较函数是function(_a,_b){return _a.a === _ b.a && _a.b === __ b.b;},那么该数组将不会不能排序。

– graham.reeds
2010-3-25在6:02

那是无效的比较功能。从developer.mozilla.org/en/Core_JavaScript_1.5_Reference/…...函数compare(a,b){如果(按某种排序标准,a小于b)则返回-1;如果(按排序标准,a大于b)返回1; // a必须等于b返回0; } ...

–maccullt
2010-3-25在17:01

#12 楼

这是执行此操作的通用方法:传递一个函数来测试数组的两个元素是否相等。在这种情况下,它将比较要比较的两个对象的nameplace属性的值。

ES5回答




 function removeDuplicates(arr, equals) {
    var originalArr = arr.slice(0);
    var i, len, val;
    arr.length = 0;

    for (i = 0, len = originalArr.length; i < len; ++i) {
        val = originalArr[i];
        if (!arr.some(function(item) { return equals(item, val); })) {
            arr.push(val);
        }
    }
}

function thingsEqual(thing1, thing2) {
    return thing1.place === thing2.place
        && thing1.name === thing2.name;
}

var things = [
  {place:"here",name:"stuff"},
  {place:"there",name:"morestuff"},
  {place:"there",name:"morestuff"}
];

removeDuplicates(things, thingsEqual);
console.log(things); 





原始ES3答案

function arrayContains(arr, val, equals) {
    var i = arr.length;
    while (i--) {
        if ( equals(arr[i], val) ) {
            return true;
        }
    }
    return false;
}

function removeDuplicates(arr, equals) {
    var originalArr = arr.slice(0);
    var i, len, j, val;
    arr.length = 0;

    for (i = 0, len = originalArr.length; i < len; ++i) {
        val = originalArr[i];
        if (!arrayContains(arr, val, equals)) {
            arr.push(val);
        }
    }
}

function thingsEqual(thing1, thing2) {
    return thing1.place === thing2.place
        && thing1.name === thing2.name;
}

removeDuplicates(things.thing, thingsEqual);


评论


即使两个对象共享相同的属性和值,它们的求值也不相等。

–肯尼贝克
2010-2-8在4:06

是的我知道。但是,公平地说,我没有正确理解这个问题:我没有发现它是具有相同属性的对象,他需要清除掉这些属性。我将编辑答案。

– Tim Down
2010-2-8在9:14

而不是在arrayContains内部使用,而是使用Array.prototype..some方法如果数组成员之一匹配条件,则返回true

– MarkosyanArtur
18 Mar 5 '18在6:29



#13 楼

要再添加一个到列表中。将ES6和Array.reduceArray.find结合使用。
在本示例中,基于guid属性过滤对象。

let filtered = array.reduce((accumulator, current) => {
  if (! accumulator.find(({guid}) => guid === current.guid)) {
    accumulator.push(current);
  }
  return accumulator;
}, []);


扩展此属性以允许选择属性并将其压缩到一个衬里中:

const uniqify = (array, key) => array.reduce((prev, curr) => prev.find(a => a[key] === curr[key]) ? prev : prev.push(curr) && prev, []);


>要使用它,请将对象数组和要删除重复项的键的名称作为字符串值传递:

const result = uniqify(myArrayOfObjects, 'guid')


#14 楼

TypeScript解决方案

这将删除重复的对象并保留对象的类型。

function removeDuplicateObjects(array: any[]) {
  return [...new Set(array.map(s => JSON.stringify(s)))]
    .map(s => JSON.parse(s));
}


评论


这是伟大的和短暂的!

– mojjj
19年10月2日在13:18

使用type完全破坏了TypeScript的目的

–leonheess
12月11日10:14

#15 楼

这个答案可能不会被任何人找到,但是这是一种比已有的50多个答案更好的运行时ES6简短方法:
let ids = array.map(o => o.id)
let filtered = array.filter(({id}, index) => !ids.includes(id, index+1))


示例:



 let arr = [{id: 1, name: 'one'}, {id: 2, name: 'two'}, {id: 1, name: 'one'}]

let ids = arr.map(o => o.id)
let filtered = arr.filter(({id}, index) => !ids.includes(id, index + 1))

console.log(filtered) 




它是如何工作的:
Array.filter()通过检查先前映射的id数组是否包含当前id来删除所有重复的对象({id}将对象分解为仅其id)。若要仅过滤出实际的重复项,请使用Array.includes()的第二个参数fromIndexindex + 1,它将忽略当前对象和所有以前的对象。
由于filter回调方法的每次迭代都只会搜索从当前索引开始的数组+ 1,这也大大减少了运行时间,因为只检查了以前未过滤的对象。
这显然也适用于任何其他称为id的键,甚至多个或所有键。

#16 楼

亲爱的孩子们,让我们把这东西压碎,为什么不呢?




 let uniqIds = {}, source = [{id:'a'},{id:'b'},{id:'c'},{id:'b'},{id:'a'},{id:'d'}];
let filtered = source.filter(obj => !uniqIds[obj.id] && (uniqIds[obj.id] = true));
console.log(filtered);
// EXPECTED: [{id:'a'},{id:'b'},{id:'c'},{id:'d'}]; 




评论


这不能回答原始问题,因为这是在搜索ID。问题需要整个对象在所有字段(例如,地点和名称)中都是唯一的

– L. Holanda
19年10月10日在18:55

这是对问题的上述概括的改进。最初的问题发布于9年前,因此最初的发布者可能并不担心今天的位置和名称。任何阅读此线程的人都在寻找一种优化方法来简化对象列表,这是一种紧凑的方法。

–悬崖厅
19年12月11日在19:25

#17 楼

您还可以使用Map

const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());


完整样本:

const things = new Object();

things.thing = new Array();

things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});

const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());

console.log(JSON.stringify(dedupThings, null, 4));


结果:

[
    {
        "place": "here",
        "name": "stuff"
    },
    {
        "place": "there",
        "name": "morestuff"
    }
]


评论


+1,不错,可以进一步解释dedup的内部工作情况将很不错-从好的方面来说,我现在知道reduce:D

– MimiEAM
17-4-6在12:53



#18 楼

考虑到lodash.uniqWith

var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];

_.uniqWith(objects, _.isEqual);
// => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]


评论


lodash的uniq或uniqBy都没有做到这一点,但是您的解决方案做到了。谢谢!但是,如果是直接复制,请提供代码源。 lodash.com/docs/4.17.10#uniqWith

– Manu CJ
18 Jun 18'在8:21



#19 楼




 let myData = [{place:"here",name:"stuff"}, 
 {place:"there",name:"morestuff"},
 {place:"there",name:"morestuff"}];


let q = [...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];

console.log(q) 





使用ES6和new Map()的单缸套。

// assign things.thing to myData
let myData = things.thing;

[...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];


详细信息:-


在数据列表上执行.map()并将每个对象转换为一个[key, value]对数组(长度= 2),第一个元素(键)将是对象的stringified版本,第二个(值)将是object本身。 br />将上面创建的数组列表添加到new Map()会将键作为stringified对象,任何相同的键添加都会导致覆盖现有键。
使用.values()将为MapIterator提供Map中的所有值(在本例中为obj
最后,spread ...运算符使用上述步骤中的值提供新的数组。


#20 楼

另一个选择是创建一个自定义indexOf函数,该函数将比较每个对象所选属性的值并将其包装在reduce函数中。

var uniq = redundant_array.reduce(function(a,b){
      function indexOfProperty (a, b){
          for (var i=0;i<a.length;i++){
              if(a[i].property == b.property){
                   return i;
               }
          }
         return -1;
      }

      if (indexOfProperty(a,b) < 0 ) a.push(b);
        return a;
    },[]);


评论


这对我来说非常有用-我将其与lodash.isequal npm软件包配对,作为轻量级的对象比较器来执行唯一的数组过滤...例如不同的对象数组。只是交换if(_.isEqual(a [i],b)){而不是寻找@单个属性

– SliverNinja-MSFT
17年11月29日在17:40

#21 楼

我知道这个问题已经有很多答案了,但是请忍受...
您数组中的某些对象可能具有您不感兴趣的其他属性,或者您只是想找到唯一的属性对象仅考虑属性的子集。
考虑下面的数组。假设您要仅考虑propOnepropTwo来查找此数组中的唯一对象,而忽略那里可能存在的任何其他属性。
预期的结果应仅包括第一个和最后一个对象。因此代码如下:



 const array = [{
    propOne: 'a',
    propTwo: 'b',
    propThree: 'I have no part in this...'
},
{
    propOne: 'a',
    propTwo: 'b',
    someOtherProperty: 'no one cares about this...'
},
{
    propOne: 'x',
    propTwo: 'y',
    yetAnotherJunk: 'I am valueless really',
    noOneHasThis: 'I have something no one has'
}];

const uniques = [...new Set(
    array.map(x => JSON.stringify((({ propOne, propTwo }) => ({ propOne, propTwo }))(x))))
].map(JSON.parse);

console.log(uniques); 




评论


它可以工作,但其他属性将被清除,是否可以保留所选对象的其余属性?

– Thanwa Ch。
9月19日11:43

@ThanwaCh。这是可行的,而且确实是一个优先事项-仅需要确定在重复的情况下应从其余对象中获取哪个对象。以我的示例为例,数组中的第一个和第二个对象成为唯一的对象。现在,该对象应该包含来自array [0]的propThree,还是来自array [1]的someOtherProperty,或者两者都包含,还是其他?只要我们确切知道在这种情况下该怎么办,您所要求的就可以确定。

–Sнаđошƒаӽ
9月19日12:05



#22 楼

这样对我来说效果很好:
 function arrayUnique(arr, uniqueKey) {
  const flagList = new Set()
  return arr.filter(function(item) {
    if (!flagList.has(item[uniqueKey])) {
      flagList.add(item[uniqueKey])
      return true
    }
  })
}
const data = [
  {
    name: 'Kyle',
    occupation: 'Fashion Designer'
  },
  {
    name: 'Kyle',
    occupation: 'Fashion Designer'
  },
  {
    name: 'Emily',
    occupation: 'Web Designer'
  },
  {
    name: 'Melissa',
    occupation: 'Fashion Designer'
  },
  {
    name: 'Tom',
    occupation: 'Web Developer'
  },
  {
    name: 'Tom',
    occupation: 'Web Developer'
  }
]
console.table(arrayUnique(data, 'name'))// work well
 

printout
 ┌─────────┬───────────┬────────────────────┐
│ (index) │   name    │     occupation     │
├─────────┼───────────┼────────────────────┤
│    0    │  'Kyle'   │ 'Fashion Designer' │
│    1    │  'Emily'  │   'Web Designer'   │
│    2    │ 'Melissa' │ 'Fashion Designer' │
│    3    │   'Tom'   │  'Web Developer'   │
└─────────┴───────────┴────────────────────┘
 

ES5:
 function arrayUnique(arr, uniqueKey) {
  const flagList = []
  return arr.filter(function(item) {
    if (flagList.indexOf(item[uniqueKey]) === -1) {
      flagList.push(item[uniqueKey])
      return true
    }
  })
}
 

这两种方法更简单易懂。

#23 楼




  const things = [
  {place:"here",name:"stuff"},
  {place:"there",name:"morestuff"},
  {place:"there",name:"morestuff"}
];
const filteredArr = things.reduce((thing, current) => {
  const x = thing.find(item => item.place === current.place);
  if (!x) {
    return thing.concat([current]);
  } else {
    return thing;
  }
}, []);
console.log(filteredArr) 




#24 楼

removeDuplicates()接收一个对象数组,并返回一个没有任何重复对象的新数组(基于id属性)。
const allTests = [
  {name: 'Test1', id: '1'}, 
  {name: 'Test3', id: '3'},
  {name: 'Test2', id: '2'},
  {name: 'Test2', id: '2'},
  {name: 'Test3', id: '3'}
];

function removeDuplicates(array) {
  let uniq = {};
  return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true))
}

removeDuplicates(allTests);

预期结果:
[
  {name: 'Test1', id: '1'}, 
  {name: 'Test3', id: '3'},
  {name: 'Test2', id: '2'}
];

首先,我们将变量uniq的值传递给一个空对象。
接下来,我们筛选对象数组。筛选器会创建一个新数组,其中的所有元素都将通过提供的功能实现的测试。
return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));

上面,我们使用&&的短路功能。如果&&的左侧求值为true,则返回&&右侧的值。如果左侧为false,则返回&&左侧的内容。
对于每个对象(obj),我们都会在uniq中检查名为obj.id值的属性(在这种情况下,第一个迭代将检查属性“ 1”。)我们想要它返回的结果相反(true或false),这就是为什么要使用!的原因。在!uniq[obj.id]中。如果uniq已经具有id属性,则它返回true,结果为false(!),告诉过滤器函数不要添加该obj。但是,如果找不到obj.id属性,它将返回false,然后求值为true(!),并返回&&右侧的所有内容,或者返回(uniq [obj.id] = true)。这是一个真实值,它告诉filter方法将该obj添加到返回的数组中,并且还将属性{1:true}添加到uniq中。这样可以确保不会再添加任何其他具有相同ID的obj实例。

评论


也许解释一下您的代码,它如何回答问题?

– mix3d
18年8月28日在20:56

谢谢,mix3d。我增加了澄清。

– MarkN
18年8月29日在22:22

感谢您的解释!这个解决方案对我有用,并且与这里发布的其他几个类似,尽管我不知道发生了什么:)

–蒂姆·莫洛伊(Tim Molloy)
19 Mar 7 '19 at 19:37

#25 楼

这是ES6的解决方案,您只想保留最后一项。此解决方案符合功能并符合Airbnb风格。
const things = {
  thing: [
    { place: 'here', name: 'stuff' },
    { place: 'there', name: 'morestuff1' },
    { place: 'there', name: 'morestuff2' }, 
  ],
};

const removeDuplicates = (array, key) => {
  return array.reduce((arr, item) => {
    const removed = arr.filter(i => i[key] !== item[key]);
    return [...removed, item];
  }, []);
};

console.log(removeDuplicates(things.thing, 'place'));
// > [{ place: 'here', name: 'stuff' }, { place: 'there', name: 'morestuff2' }]


评论


您可以删除重复项,也可以使用此代码删除所有重复项。好啊

– sg28
19年5月23日在18:02

#26 楼

通过Set对象解决方案



 const seen = new Set();
 const things = [
  {place:"here",name:"stuff"},
  {place:"there",name:"morestuff"},
  {place:"there",name:"morestuff"}
];

const filteredArr = things.filter(el => {
  const duplicate = seen.has(el.place);
  seen.add(el.place);
  return !duplicate;
});
console.log(filteredArr) 




Set对象功能
Set对象中的每个值都必须是唯一的,将检查值的相等性。
Set对象根据数据类型存储唯一值的目的,无论是原始值还是对象引用。它都有四个非常有用的实例方法addclearhasdelete
唯一性和数据类型功能:..
add方法
将唯一数据推送到集合中默认情况下还保留数据类型..这意味着它防止将重复项推入集合中,并且默认情况下还将检查数据类型...
has方法
有时需要检查数据项是否存在于集合中并。这是用于收集唯一ID或项目以及数据类型的便捷方法。
delete方法
通过标识数据类型将从集合中删除特定项目。.
clear方法
它将从一个特定变量中删除所有集合项,并设置为空对象。
Set对象还具有迭代方法和更多功能。.
更好地从这里读:Set-JavaScript | MDN

#27 楼

let data = [
  {
    'name': 'Amir',
    'surname': 'Rahnama'
  }, 
  {
    'name': 'Amir',
    'surname': 'Stevens'
  }
];
let non_duplicated_data = _.uniqBy(data, 'name');


评论


请围绕您的代码添加说明,以便将来的访问者可以了解您在做什么。谢谢。

–错误
17年6月20日在8:06

#28 楼

继续探索从对象数组中删除重复项的ES6方法:将thisArgArray.prototype.filter参数设置为new Set提供了一个不错的选择:




 const things = [
  {place:"here",name:"stuff"},
  {place:"there",name:"morestuff"},
  {place:"there",name:"morestuff"}
];

const filtered = things.filter(function({place, name}) {

  const key =`${place}${name}`;

  return !this.has(key) && this.add(key);

}, new Set);

console.log(filtered); 





但是,它不能与箭头函数() =>一起使用,因为this已绑定到其词法范围。

#29 楼

使用ES6“减少”和“查找”数组辅助方法的简单解决方案

有效且完美地工作!

#30 楼

我相信将reduceJSON.stringify结合使用以完美比较对象并有选择地添加那些尚未在累加器中的对象是一种优雅的方法。

请记住,JSON.stringify在极端情况下可能会成为性能问题数组有很多对象,并且它们很复杂,但是大多数时候都是这样,这是恕我直言的最短方法。




 var collection= [{a:1},{a:2},{a:1},{a:3}]

var filtered = collection.reduce((filtered, item) => {
  if( !filtered.some(filteredItem => JSON.stringify(filteredItem) == JSON.stringify(item)) )
    filtered.push(item)
  return filtered
}, [])

console.log(filtered) 





另一种相同的编写方式(但效率较低):

collection.reduce((filtered, item) => 
  filtered.some(filteredItem => 
    JSON.stringify(filteredItem ) == JSON.stringify(item)) 
      ? filtered
      : [...filtered, item]
, [])