如何从对象数组中删除所有重复项?

How to remove all duplicates from an array of objects?

提问人:Travis 提问时间:2/8/2010 最后编辑:chickensTravis 更新时间:10/9/2023 访问量:1201371

问:

我有一个对象,它包含一个对象数组。

obj = {};

obj.arr = new Array();

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

我想知道从数组中删除重复对象的最佳方法是什么。因此,例如,将变成...obj.arr

{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}
JavaScript 数组 对象 重复

评论

0赞 Matthew Lock 2/8/2010
你的意思是你如何停止将所有相同参数添加到数组中的哈希表/对象?
11赞 Travis 2/8/2010
Mathew -> 如果一开始就防止将重复对象添加到数组中,而不是稍后将其过滤掉,那么是的,那也没问题。
3赞 tonkatata 12/7/2021
Suuuper 很长的答案,但 MDN 可能是最短的:arrayWithNoDuplicates = Array.from(new Set(myArray))
10赞 Debu Shinobi 12/14/2021
@tonkatata 这不适用于对象数组。
2赞 qyb2zm302 10/18/2023
感谢@tonkatata的灵感。对象数组可以用Array.from(new Set(myArray.map(e => JSON.stringify(e)))))

答:

201赞 aefxx 2/8/2010 #1

一个原始的方法是:

const obj = {};

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

things.thing = new Array();

 for (const key in obj) { 
   things.thing.push(obj[key]);
}

评论

78赞 Nosebleed 8/26/2014
你永远不应该在 for 循环中使用长度,因为它会在每次迭代中减慢计算它的速度。将其分配给循环外部的变量,并传递该变量而不是 things.thing.length。
16赞 Kuan 6/24/2015
@aefxx我不太了解这个功能,你如何处理“地方”相同但名称不同的情况,是否应该考虑dup?
2赞 Deepak G M 4/17/2019
虽然这有效,但它不能处理排序的数组,因为获取键永远无法保证顺序。所以,你最终会再次对它进行排序。现在,假设数组没有排序,但它的顺序很重要,你无法确保顺序保持不变
3赞 aefxx 4/18/2019
@DeepakGM 你说得很对。答案不会(不一定)保留给定的顺序。如果这是一个要求,应该寻找另一种解决方案。
0赞 Ryan H 2/9/2020
如何修改上述内容以从包含 X 的数组中删除对象以及重复数据删除的对象?
26赞 Tim Down 2/8/2010 #2

这是一种通用的执行方法:您传入一个函数,用于测试数组的两个元素是否相等。在本例中,它比较被比较的两个对象的 和 属性的值。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);

评论

1赞 kennebec 2/8/2010
两个对象的计算结果不相等,即使它们共享相同的属性和值也是如此。
0赞 Tim Down 2/8/2010
是的,我明白。但公平地说,我没有正确阅读这个问题:我没有发现他需要清除的是具有相同属性的物体。我会编辑我的答案。
1赞 MarkosyanArtur 3/5/2018
而不是 while inside arrayContains- 使用 Array.prototype..some 方法 如果其中一个数组成员与条件匹配,则返回 true
25赞 maccullt 2/8/2010 #3

如果可以等到所有添加后才消除重复项,则典型的方法是先对数组进行排序,然后再消除重复项。排序避免了在遍历每个元素时扫描数组中的 N * N 方法。

“消除重复项”功能通常称为 uniqueuniq。一些现有的实现可能会将这两个步骤结合起来,例如,原型的 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;
    }

评论

0赞 Tim Down 2/8/2010
这不适用于没有自然排序顺序的通用对象。
0赞 maccullt 2/8/2010
没错,我添加了用户提供的比较版本。
0赞 graham.reeds 3/25/2010
用户提供的比较版本将不起作用,因为如果您的比较函数是,则不会对数组进行排序。function(_a,_b){return _a.a===_b.a && _a.b===_b.b;}
1赞 maccullt 3/26/2010
这是一个无效的比较函数。从 developer.mozilla.org/en/Core_JavaScript_1.5_Reference/... ...function compare(a, b) { if (a is less than b by some ordererion) return -1; if (a is greater than b by the order criteria) return 1; // a must equal to b return 0; } ...
158赞 ambodi 11/26/2014 #4

如果你可以使用 Javascript 库,比如 underscore 或 lodash,我建议你看看它们库中的函数。从:_.uniqlodash

_.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

评论

4赞 ambodi 1/25/2015
@Praveen Pds:我在代码示例中说过下划线吗?我说“lodash”有这个功能,下划线也有类似的功能。在投票之前,请仔细阅读答案。
0赞 praveenpds 1/26/2015
使用 _underscore.js 列出唯一对象 holdingObject = _.uniq(holdingObject , function(item, key, name) { return item.name;
39赞 drmrbrewer 6/14/2017
注意:您现在需要使用 代替 ,例如 ...文档:lodash.com/docs#uniqByuniqByuniq_.uniqBy(data, 'name')
0赞 Stas Sorokin 1/2/2022
如果你有一个很深的收藏:做:let data = [{'v': {'t':1, 'name':"foo"}}, {'v': {'t':1, 'name':"bar"}}];let uniq = _.uniqBy(data, 'v.t');
5赞 ZeroSum 7/3/2015 #5

另一种选择是创建一个自定义 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;
    },[]);

评论

0赞 SliverNinja - MSFT 11/30/2017
这对我来说效果很好 - 我将其与 lodash.isequal npm 包配对作为轻量级对象比较器以执行独特的数组过滤......例如,不同的对象数组。只是换了进去,而不是看@一个属性if (_.isEqual(a[i], b)) {
30赞 Alex Kobylinski 3/26/2016 #6

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

    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');

评论

0赞 Sterex 7/13/2016
虽然这有一个大于 O(n²) 的阶数,但这符合我的用例,因为我的数组大小总是小于 30。谢谢!
947赞 Eydrian 4/20/2016 #7

使用一些 ES6 魔法怎么样?

obj.arr = obj.arr.filter((value, index, self) =>
  index === self.findIndex((t) => (
    t.place === value.place && t.name === value.name
  ))
)

引用 URL

更通用的解决方案是:

const uniqueArray = obj.arr.filter((value, index) => {
  const _value = JSON.stringify(value);
  return index === obj.arr.findIndex(obj => {
    return JSON.stringify(obj) === _value;
  });
});

使用上述属性策略代替:JSON.stringify

const isPropValuesEqual = (subject, target, propNames) =>
  propNames.every(propName => subject[propName] === target[propName]);

const getUniqueItemsByProperties = (items, propNames) => 
  items.filter((item, index, array) =>
    index === array.findIndex(foundItem => isPropValuesEqual(foundItem, item, propNames))
  );

如果希望属性是数组或值,则可以添加包装器:propNames

const getUniqueItemsByProperties = (items, propNames) => {
  const propNamesArray = Array.from(propNames);

  return items.filter((item, index, array) =>
    index === array.findIndex(foundItem => isPropValuesEqual(foundItem, item, propNamesArray))
  );
};

允许两者和getUniqueItemsByProperties('a')getUniqueItemsByProperties(['a']);

Stackblitz 示例

解释

  • 首先了解使用的两种方法:
  • 接下来,了解是什么让两个对象相等,并牢记这一点。
  • 如果某物满足我们刚才想到的标准,我们可以将其检测为重复项,但它的位置不是具有该标准的对象的第一个实例。
  • 因此,我们可以使用上述标准来确定某些内容是否重复。

评论

115赞 Josh Cole 3/13/2017
这可以缩短为:things.thing = things.thing.filter((thing, index, self) => self.findIndex(t => t.place === thing.place && t.name === thing.name) === index)
10赞 Eydrian 7/18/2018
@vsync把@BKM的答案放在一起,一个通用的解决方案是:jsfiddle.net/x9ku0p7L/28const uniqueArray = arrayOfObjects.filter((object,index) => index === arrayOfObjects.findIndex(obj => JSON.stringify(obj) === JSON.stringify(object)));
33赞 JBaczuk 9/14/2019
这里的关键是 findIndex() 方法返回第一个元素的索引,因此如果有第二个元素匹配,则在过滤器期间永远不会找到并添加它。我盯着它看了一分钟:)
4赞 Jose A 4/23/2020
一个问题,这难道不是 O(n^2) 方法吗?如果我处理 30 条记录,我会做 900 次迭代,对吧?(最坏情况,没有重复)
11赞 JP_ 2/24/2021
如果您有一个包含 200,000 个条目的数组,那么这将需要 400 亿次迭代。这绝不应用于大型数组。始终使用地图。
98赞 James-Jesse Drinkard 7/27/2016 #8

我有完全相同的要求,根据单个字段上的重复项删除数组中的重复对象。我在这里找到了代码: 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"}]

评论

1赞 Abdul Sadik Yalcin 11/7/2017
如果该函数也可以过滤“falsy”对象,这将更有用。for(var i in array) { if(array[i][prop]){ //valid lookupObject[array[i][prop]] = array[i]; } else { console.log('falsy object'); } }
0赞 Tudor B. 2/18/2019
为什么不通过使用以下方法降低复杂度 0(n):for (let i in originalArray) { if (lookupObject[originalArray[i]['id']] === undefined) { newArray.push(originalArray[i]); } lookupObject[originalArray[i]['id']] = originalArray[i]; }
0赞 Christian Matthew 9/17/2019
这是最好的方法,因为重要的是要知道您希望不被复制的内容。现在这可以通过e6标准的减速器来完成吗?
15赞 Pragmateek 2/26/2017 #9

您还可以使用: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"
    }
]

评论

0赞 MimiEAM 4/6/2017
+1,很高兴更多地解释 dedup 的内部工作会很好 - 从好的方面来说,我现在明白了减少:D
1赞 Farhad 10/20/2021
很棒的一行答案,终于我看到了地图:D的用法
72赞 Mμ. 6/17/2017 #10

一个衬垫使用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)

解释:

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

评论

27赞 PirateApp 10/2/2017
问题是 {a: 1, b:2} 不等于 {b:2,a:1}
4赞 MarkosyanArtur 3/5/2018
请记住,Date 属性会存在问题
0赞 B1K 10/17/2018
此行使用原始对象数组中不存在的行对象创建随机 null 值。你能帮忙吗?
0赞 knot22 5/25/2022
为了解决@PirateApp在评论中指出的问题,可以按如下方式修改@Mu提供的答案,以处理具有重新排列属性的对象: 然后,在调用时,只需传入元素数组的属性名称即可。对于原始帖子,这将是 .对于@PirateApp的例子,这将是.const distinct = (data, elements = []) => [...new Set(data.map(o => JSON.stringify(o, elements)))].map(o => JSON.parse(o));distinct['place', 'name']['a', 'b']
17赞 Justin 10/20/2017 #11

考虑 lodash.uniqWith

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

评论

1赞 Manu CJ 6/18/2018
lodash 的 uniq 和 uniqBy 都没有解决问题,但您的解决方案做到了。谢谢!但是,如果是直接复制,请提供代码的来源。 lodash.com/docs/4.17.10#uniqWith
16赞 Pete B 2/14/2018 #12

向列表中再添加一个。使用 ES6 和 .
在此示例中,基于属性筛选对象。
Array.reduceArray.findguid

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')
15赞 Cliff Hall 3/15/2018 #13

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

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

评论

1赞 L. Holanda 12/11/2019
这并不能回答原始问题,因为这是搜索 .该问题要求整个对象在所有字段中都是唯一的,例如 和idplacename
2赞 Cliff Hall 12/12/2019
这是对上述问题的概括的改进。最初的问题发布于 9 年前,所以原始海报可能并不担心,今天。任何阅读此线程的人都在寻找一种最佳方法来重复对象列表,这是一种紧凑的方式。placename
61赞 sravan ganji 4/29/2018 #14

ES6 one liner 来了

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

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

评论

6赞 David Barker 5/30/2019
如果您只想删除具有单个重复值的对象,则很好且干净,对于完全重复的对象则不那么干净。
0赞 sravan ganji 9/2/2020
@DavidBarker是指一个对象的多个重复值?
0赞 David Barker 9/2/2020
是的,但更具体地说是具有所有相同值的对象。
1赞 Jonathan Arias 2/18/2021
in 的功能是什么?我不明白这段代码。:curcur.id]:cur
1赞 Heretic Monkey 1/27/2022
与往常一样,代码的解释很好。
8赞 Micah 7/24/2018 #15

这是 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' }]

评论

0赞 sg28 5/24/2019
您可以使用此代码删除重复项,也可以删除所有重复项。好
12赞 MarkN 8/29/2018 #16

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 的值设置为空对象。

接下来,我们过滤对象数组。Filter 创建一个新数组,其中包含通过所提供函数实现的测试的所有元素。

return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));

上面,我们使用 && 的短路功能。如果 && 的左侧计算结果为 true,则返回 && 右侧的值。如果左侧为 false,则返回 &&.

对于每个对象(obj),我们检查uniq中是否有一个名为 obj.id 值的属性(在本例中,在第一次迭代中,它将检查属性“1”。我们想要与它返回的结果相反(无论是 true 还是 false),这就是我们使用 !在。如果 uniq 已经具有 id 属性,则返回 true,其计算结果为 false (!),告诉过滤器函数不要添加该 obj。但是,如果它没有找到 obj.id 属性,则返回 false,然后计算结果为 true (!) 并返回 &&或 (uniq[obj.id] = true) 右侧的所有内容。这是一个真实值,告诉过滤器方法将该 obj 添加到返回的数组中,并且它还将属性 {1: true} 添加到 uniq。这样可以确保不会再次添加具有相同 id 的任何其他 obj 实例。!uniq[obj.id]

46赞 аlex 9/11/2018 #17

要从对象数组中删除所有重复项,最简单的方法是使用: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);

评论

12赞 Samuel Liew 9/15/2018
在 Stack Overflow 上,最好添加一个解释,说明为什么你的解决方案应该有效,尤其是你的解决方案如何比其他答案更好。有关更多信息,请阅读如何回答
1赞 L. Holanda 12/11/2019
这并不能回答原始问题,因为这是搜索 .该问题要求整个对象在所有字段中都是唯一的,例如 和idplacename
15赞 Savan Akbari 4/10/2019 #18

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()];

详:-

  1. 在数据列表上执行操作并将每个单独的对象转换为一对数组(length =2),第一个元素(键)将是对象的版本,第二个元素(值)将是一个本身。.map()[key, value]stringifiedobject
  2. 添加上面创建的数组列表会将键作为对象,并且任何相同的键添加都会导致覆盖已经存在的键。new Map()stringified
  3. 使用将为 MapIterator 提供 Map 中的所有值(在我们的例子中).values()obj
  4. 最后,运算符使用上述步骤中的值给出新的数组。spread ...
14赞 user6269864 4/26/2019 #19

TypeScript 解决方案

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

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

评论

7赞 leonheess 12/11/2020
使用类型完全违背了 TypeScript 的目的any
0赞 Neil 1/25/2021
当然,我认为这消除了税务编译器将进行的任何推断检查。
5赞 MattSom 11/8/2021
当你有对象时,停止使用 stringify!天哪,这就是我不喜欢JS的原因,它让人们做各种丑陋的事情。
1赞 Heretic Monkey 1/27/2022
如果数组包含任何具有循环引用的对象,则此代码将在其表面上落空。
506赞 chickens 6/25/2019 #20

一个带过滤器的衬里 ( 保存顺序 )

在数组中查找唯一值。id

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

如果顺序不重要,地图解决方案会更快:使用地图的解决方案


由多个属性唯一 ( 和placename )

arr.filter((v,i,a)=>a.findIndex(v2=>['place','name'].every(k=>v2[k] ===v[k]))===i)

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

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

通过将 替换为 来保留上次出现的情况。findIndexfindLastIndex

arr.filter((v,i,a)=>a.findLastIndex(v2=>(v2.place === v.place))===i)

评论

54赞 James B 10/10/2020
v,i,a == 值、索引、数组
0赞 Arriel 2/2/2021
这对于查找我的 vue 模态中的键值对是否有重复项非常有效。+1
8赞 Jamal Hussain 3/19/2021
arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i) 如果键的顺序不同,这将不起作用
1赞 avalanche1 11/30/2021
简直太美了
5赞 Heretic Monkey 1/27/2022
如果它对这些作用有解释,那就更好了。如果他们使用清晰的命名约定,而不是试图预先缩小代码。
310赞 V. Sambor 6/26/2019 #21

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

const key = 'place';
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 将消除重复的键值,仅保留同一键的最后插入值。注意:地图会保留插入顺序。(检查地图和对象之间的区别)

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

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

新地图(mappedArr).values()

最后一个是将这些值添加到一个新的数组中,以便它可以看起来像初始结构并返回:

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

评论

0赞 L. Holanda 12/11/2019
这并不能回答原始问题,因为这是搜索 .该问题要求整个对象在所有字段中都是唯一的,例如 和idplacename
0赞 David Schumann 3/26/2020
您的 ES6 函数看起来非常简洁实用。你能再解释一下吗?到底发生了什么?是否删除了第一个或最后一个重复项?或者是随机的,哪个重复项被删除?那会很有帮助,谢谢。
0赞 David Schumann 3/26/2020
据我所知,创建了一个以属性值为键的地图。但这不是 100% 如何或是否保留数组的顺序。
3赞 V. Sambor 3/26/2020
嗨,@DavidSchumann,我将更新答案并解释它是如何工作的。但是对于简短的回答,订单被保留,第一个被删除......想想它是如何插入到地图中的......它会检查密钥是否已经存在,它会更新它,因此最后一个密钥将保留
8赞 readikus 8/16/2022
TS 版本,以防有人在寻找:export const unique = <T extends { [key: string]: unknown }>(arr: T[], key: string): T[] => [ ...new Map(arr.map((item: T) => [item[key], item])).values() ];
9赞 JackChouMine 1/9/2020 #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

打印输出

┌─────────┬───────────┬────────────────────┐
│ (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
    }
  })
}

这两种方法更简单,更容易理解。

6赞 Sнаđошƒаӽ 9/17/2020 #23

我知道这个问题已经有很多答案了,但请耐心等待......

数组中的某些对象可能具有您不感兴趣的其他属性,或者您只想查找仅考虑属性子集的唯一对象。

考虑下面的数组。假设您想在此数组中查找仅考虑 和 的唯一对象,并忽略可能存在的任何其他属性。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(((o) => ({
        propOne: o.propOne,
        propTwo: o.propTwo
    }))(x))))
].map(JSON.parse);

console.log(uniques);

评论

0赞 Thanwa Ch. 9/19/2020
它可以工作,但其他属性将被清除,是否可以保留所选对象的其余属性?
0赞 Sнаđошƒаӽ 9/19/2020
@ThanwaCh。这是可行的,这确实是一个偏好问题 - 只需要确定在重复的情况下应该从哪个对象获取其余属性。使用我的例子,第一个和第二个对象在 .现在,该对象应该包含 from 、 或 from 、 或两者兼而有之,还是其他内容?只要我们确切地知道在这种情况下该怎么做,您要求的肯定是可行的。arrayuniquespropThreearray[0]someOtherPropertyarray[1]
0赞 knot22 5/26/2021
这个解决方案对于我正在编码的用例来说效果很好。你能解释一下这部分是什么/做什么吗?(({ propOne, propTwo }) => ({ propOne, propTwo }))(x)
1赞 Sнаđошƒаӽ 5/26/2021
前面的部分@knot22是一个箭头函数,它将参数对象解压缩为 properties 和 。在此处了解对象解构。现在我又读了一遍代码,我认为它应该写得更清楚一些。我已经更新了代码。(x)propOnepropTwo
13赞 نور 9/25/2020 #24

 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)

通过设置对象的解决方案 |根据数据类型

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 Object 中的每个值都必须是唯一的,将检查值相等性

设置对象存储唯一值的目的根据数据类型,无论是原始值还是对象 references.it 都有非常有用的四种实例方法 addclearhasdelete

唯一和数据类型功能:..

Add方法

默认情况下,它将唯一数据推送到集合中,并保留数据类型。这意味着它可以防止将重复的项目推送到集合中,默认情况下它也会检查数据类型......

has 方法

有时需要检查数据项是否存在于集合中,并且。这是集合的唯一 ID 或项目和数据类型的便捷方法。

Delete 方法

它将通过标识数据类型从集合中删除特定项目。

clear 方法

它将从一个特定变量中删除所有集合项,并设置为空对象

设置对象还具有迭代方法和更多功能。

最好从这里阅读:Set - JavaScript |MDN网络

248赞 leonheess 11/12/2020 #25

简单且高性能的解决方案,具有比现有 70+ 答案更好的运行时间:

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

例:

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

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

console.log(filtered);

工作原理:

Array.filter() 通过检查先前映射的 id-array 是否包含当前 id(将对象破坏为仅将其 id 破坏)来删除所有重复的对象。为了只过滤掉实际的重复项,它使用 Array.includes() 的第二个参数,该参数将忽略当前对象和所有以前的对象。{id}fromIndexindex + 1

由于回调方法的每次迭代只会搜索从当前索引 + 1 开始的数组,这也大大减少了运行时间,因为只有以前未过滤的对象才会被检查。filter

如果您没有像这样的唯一标识符怎么办?id

只需创建一个临时的:

const objToId = ({ name, city, birthyear }) => `${name}-${city}-${birthyear}`;


const ids = arr.map(objToId);
const filtered = arr.filter((item, index) => !ids.includes(objToId(item), index + 1));

评论

2赞 leonheess 2/22/2021
@user239558 好问题,但不是真的,它会慢几个数量级,并且对于具有不同顺序的对象,它将无法检测到重复项。{id: 1, name: 'one'}{namd: 'one', id: 1}
1赞 Timotronadon 3/5/2021
你在这里拉的{id }有什么魔力?我正在关注其他一切。正要为我自己的目的实现一个 Set,但发现了这个
4赞 leonheess 3/5/2021
好问题,@Timotronadon。 将对象解构为仅其 -key。为了说明这一点,让我们看看这两个循环:1. 和 2。.它们都在做同样的事情:打印 中所有对象的 -键。但是,一种使用解构,另一种使用更传统的键访问,通过点表示法。{ id }idarr.forEach(object => console.log(object.id))arr.forEach({id} => console.log(id))idarr
3赞 d0rf47 11/19/2021
在这里做出最好的回应。简单、干净、优雅,像魅力一样工作,谢谢!
1赞 SatelBill 3/18/2022
惊人的答案。这在不使用任何外部库的情况下完美运行。
22赞 doğukan 2/14/2021 #26

我认为最好的方法是使用 reduceMap 对象这是一条线的解决方案。

const data = [
  {id: 1, name: 'David'},
  {id: 2, name: 'Mark'},
  {id: 2, name: 'Lora'},
  {id: 4, name: 'Tyler'},
  {id: 4, name: 'Donald'},
  {id: 5, name: 'Adrian'},
  {id: 6, name: 'Michael'}
]

const uniqueData = [...data.reduce((map, obj) => map.set(obj.id, obj), new Map()).values()];

console.log(uniqueData)

/*
  in `map.set(obj.id, obj)`
  
  'obj.id' is key. (don't worry. we'll get only values using the .values() method)
  'obj' is whole object.
*/

评论

3赞 Heretic Monkey 1/27/2022
通过删除行之间的回车和/或换行:P,可以使任何内容成为“单行解决方案”。
3赞 Aakash Haider 3/3/2021 #27

这个解决方案最适合我,通过使用 Array.from 方法,而且它更短且可读性更强。

let person = [
{name: "john"}, 
{name: "jane"}, 
{name: "imelda"}, 
{name: "john"},
{name: "jane"}
];

const data = Array.from(new Set(person.map(JSON.stringify))).map(JSON.parse);
console.log(data);
11赞 Masih Jahangiri 9/1/2021 #28

快速(运行时间更短)且类型安全的答案,适用于懒惰的 Typescript 开发人员:

export const uniqueBy = <T>( uniqueKey: keyof T, objects: T[]): T[] => {
  const ids = objects.map(object => object[uniqueKey]);
  return objects.filter((object, index) => !ids.includes(object[uniqueKey], index + 1));
} 

评论

1赞 seniorpreacher 9/14/2021
uniqueKey应该代替,以使其更精确。keyof Tstring
35赞 chickens 12/19/2021 #29

一个带地图的衬里(高性能,不保持顺序)

在数组中查找唯一的 。idarr

const arrUniq = [...new Map(arr.map(v => [v.id, v])).values()]

如果顺序很重要,请查看带过滤器的解决方案:带过滤器的解决方案


在数组中通过多个属性( 和 ) 唯一placenamearr

const arrUniq = [...new Map(arr.map(v => [JSON.stringify([v.place,v.name]), v])).values()]

数组中所有属性的唯一arr

const arrUniq = [...new Map(arr.map(v => [JSON.stringify(v), v])).values()]

将第一个匹配项保留在数组中arr

const arrUniq = [...new Map(arr.slice().reverse().map(v => [v.id, v])).values()].reverse()

评论

0赞 CodeSammich 7/19/2022
多属性解决方案完美运行。多谢!
0赞 leonheess 8/11/2023
“高性能”一定是个笑话,因为这绝不是快的。
8赞 Ghias Ali 11/3/2022 #30

如果数组包含对象,则可以使用它来删除重复项

const persons= [
      { id: 1, name: 'John',phone:'23' },
      { id: 2, name: 'Jane',phone:'23'},
      { id: 1, name: 'Johnny',phone:'56' },
      { id: 4, name: 'Alice',phone:'67' },
    ];
const unique = [...new Map(persons.map((m) => [m.id, m])).values()];

如果根据手机删除重复项,只需将 m.id 替换为 m.phone

const unique = [...new Map(persons.map((m) => [m.phone, m])).values()];