ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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"}
您的意思是如何停止将所有相同参数添加到数组中的哈希表/对象?
Mathew -> 如果首先防止重复对象被添加到数组中更简单,而不是稍后将其过滤掉,是的,那也很好。
Suuuper 的答案很长,但 MDN 的答案可能最短:arrayWithNoDuplicates = Array.from(new Set(myArray))
@tonkatata 这不适用于对象数组。

c
chickens

来点 es6 魔法怎么样?

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

Reference 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 Example

解释

首先了解使用的两种方法:filter、findIndex

过滤器,查找索引

接下来考虑是什么让你的两个对象相等并记住这一点。

我们可以将某物检测为重复,如果它满足我们刚刚想到的标准,但它的位置不是符合标准的对象的第一个实例。

因此,我们可以使用上述标准来确定某物是否是重复的。


这可以缩短为:things.thing = things.thing.filter((thing, index, self) => self.findIndex(t => t.place === thing.place && t.name === thing.name) === index)
@vsync 只需将@BKM 的答案放在一起,一个通用的解决方案是:const uniqueArray = arrayOfObjects.filter((object,index) => index === arrayOfObjects.findIndex(obj => JSON.stringify(obj) === JSON.stringify(object))); jsfiddle.net/x9ku0p7L/28
这里的关键是 findIndex() 方法返回第一个元素的索引,所以如果有第二个元素匹配,在过滤期间永远不会找到并添加它。我盯着它看了一分钟:)
一个问题,这不是 O(n^2) 方法吗?如果我处理 30 条记录,我会进行 900 次迭代,对吗? (最坏的情况,没有重复)
如果您有一个包含 200,000 个条目的数组,那么这将需要 400 亿次迭代。这不应该与大型数组一起使用。始终使用地图。
c
chickens

一个带过滤器的衬垫(保留顺序)

在数组中查找唯一的 id

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

如果顺序不重要,地图解决方案会更快:Solution with map

由多个属性(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)

保留最后一次出现,将 findIndex 替换为 findLastIndex

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

v,i,a == 值、索引、数组
这对于查找我的 vue 模态中的键值对是否重复非常有用。 +1
arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i) 如果键不在同一个顺序
findIndex 上的 t 代表什么?
简单的美丽
L
Leo

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

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

它可以放入一个函数中:

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

这是一个工作示例:

const arr = [ {地点:“这里”,名称:“x”,其他:“其他东西1”},{地点:“那里”,名称:“x”,其他:“其他东西2”},{地点:“这里”,名称:“y”,其他:“其他东西4”},{地点:“这里”,名称:“z”,其他:“其他东西5”}] function getUniqueListBy(arr, key) { return [.. .new Map(arr.map(item => [item[key], item])).values()] } const arr1 = getUniqueListBy(arr, 'place') console.log("Unique by place") 控制台。 log(JSON.stringify(arr1)) console.log("\n按名称唯一") const arr2 = getUniqueListBy(arr, 'name') console.log(JSON.stringify(arr2))

它是如何工作的

首先,数组以一种可以用作 Map. 的输入的方式重新映射

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

这意味着数组的每个项目都将转换为另一个具有 2 个元素的数组; 选定的键作为第一个元素,整个初始项作为第二个元素,这称为条目(例如 array entriesmap entries)。 here is the official doc 有一个示例,展示了如何在 Map 构造函数中添加数组条目。

放置键时的示例:

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

其次,我们将这个修改后的数组传递给 Map 构造函数,这就是神奇的事情发生了。 Map 将消除重复的键值,仅保留同一键的最后插入值。 注意:地图保持插入顺序。 (check difference between Map and object)

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

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

新地图(mappedArr).values()

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

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


这没有回答原始问题,因为这是搜索 id。该问题需要整个对象在所有字段(例如 placename)中都是唯一的
您的 ES6 函数看起来非常简洁实用。你能再解释一下吗?究竟发生了什么?是否删除了第一个或最后一个重复项?或者它是随机的,哪个重复被删除?那会很有帮助,谢谢。
据我所知,创建了一个以属性值为键的 Map。但是,如何或是否保留数组的顺序并不是 100%。
嗨@DavidSchumann,我会更新答案并解释它是如何工作的。但简而言之,订单被保留,第一个被删除......只要想想它是如何插入到地图中的......它检查密钥是否已经存在它将更新它,然后最后一个将保留
如果 null 项或某些项没有被调用的密钥,则会出现错误,对此有什么解决办法吗?
l
leonheess

简单而高效的解决方案,比现有的 70 多个答案具有更好的运行时间:

const ids = array.map(o => o.id)
const filtered = array.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(o => o.id) const filters = 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 的任何其他键、多个甚至所有键。


@user239558 好问题,但不是真的,它会慢几个数量级,对于具有不同顺序的对象,如 {id: 1, name: 'one'}{namd: 'one', id: 1},它将无法检测到重复项。
好问题,@Timotronadon。 { id }destructuring 对象仅插入其 id 键。为了说明这一点,让我们看一下这两个循环:1. arr.forEach(object => console.log(object.id)) 和 2. arr.forEach({id} => console.log(id))。它们都在做同样的事情:打印 arr 中所有对象的 id 键。但是,一种是使用解构,另一种是通过点符号使用更传统的密钥访问。
def 最好的回应在这里。简单干净优雅,像魅力一样工作谢谢!
惊人的答案。这在不使用任何外部库的情况下完美运行。
如果你有这样的东西怎么办? const arr = [{id: 1, name: 'one'}, {id: 2, name: 'two'}, {id: 1, name: 'THREE'}] 您不想放弃 id=1 的名称吗?是否可以将其保存在数组中?
B
Bricky

一个原始的方法是:

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

你永远不应该在 for 循环中使用长度,因为它会减慢每次迭代计算它的速度。将其分配给循环外的变量并传递变量而不是 things.thing.length。
@aefxx 这个函数我不是很懂,“地点”相同但名称不同的情况如何处理,是否应该考虑dup?
尽管这可行,但它不处理已排序的数组,因为从不保证获取键的顺序。所以,你最终再次对其进行排序。现在,假设数组没有排序,但它的顺序很重要,你无法确保顺序保持不变
@DeepakGM 你是绝对正确的。答案不会(必然)保留给定的顺序。如果这是一项要求,则应该寻找另一种解决方案。
如何修改上述内容以从包含 X 以及重复数据删除的数组中删除对象?
v
vsync

如果您可以使用 underscore 或 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' 有这个功能,下划线也有类似的功能。在投反对票之前,请仔细阅读答案。
//使用 _underscore.js 列出唯一的对象 holdingObject = _.uniq(holdingObject , function(item, key, name) { return item.name; });
注意:您现在需要使用 uniqBy 而不是 uniq,例如 _.uniqBy(data, 'name')... 文档:lodash.com/docs#uniqBy
如果您有深度收藏:let data = [{'v': {'t':1, 'name':"foo"}}, {'v': {'t':1, 'name':"bar"}}]; 执行:let uniq = _.uniqBy(data, 'v.t');
V
Vini.g.fer

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

因此,在我的示例中,我从数组中删除了具有重复 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));

结果:

唯一数组是:

[{"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]){ //valid lookupObject[array[i][prop]] = array[i]; } else { console.log('falsy object'); } }
为什么不使用以下方法降低复杂度 0(n):for (let i in originalArray) { if (lookupObject[originalArray[i]['id']] === undefined) { newArray.push(originalArray[i]); } lookupObject[originalArray[i]['id']] = originalArray[i]; }
这是最好的方法,因为重要的是要知道你不想被复制的是什么。现在这可以通过e6标准的reducer来完成吗?
M
Mμ.

使用 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"}); // 为简洁起见,将 things.thing 分配给 myData var myData = things.thing; things.thing = Array.from(new Set(myData.map(JSON.stringify))).map(JSON.parse);控制台.log(things.thing)

解释:

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


问题是 {a: 1, b:2} 不会等于 {b:2,a:1}
请记住,日期属性会出现问题
此行使用原始对象数组中不存在的行对象创建随机空值。你能帮忙吗?
为了解决@PirateApp 在评论中指出的问题,@Mu 提供的答案可以修改如下,以处理具有重新排列属性的对象:const distinct = (data, elements = []) => [...new Set(data.map(o => JSON.stringify(o, elements)))].map(o => JSON.parse(o)); 然后在调用 distinct 时,只需传入元素数组的属性名称。对于将是 ['place', 'name'] 的原始帖子。对于@PirateApp 的示例,['a', 'b']
s
sravan ganji

ES6 一班轮就在这里

让 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}),{})))


如果您只想删除具有单个重复值的对象,而不是完全重复的对象,那么干净整洁。
@DavidBarker您的意思是一个对象的多个重复值?
是的,但更具体地说,具有所有相同值的对象。
cur.id]:cur 中的 :cur 的功能是什么?我不明白这段代码。
使用 lodash( _ ) 我们可以使用 _.uniqBy(arr,'id') 做同样的事情
а
аlex dykyі

要从对象数组中删除所有重复项,最简单的方法是使用 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 上添加一个关于为什么你的解决方案应该有效的解释是一个很好的做法,尤其是你的解决方案如何优于其他答案。有关详细信息,请阅读 How To Answer
这没有回答原始问题,因为这是搜索 id。该问题需要整个对象在所有字段(例如 placename)中都是唯一的
A
Alex Kobylinski

如果您只需要通过对象的一个字段进行比较,这是使用 Array 迭代方法的另一种选择:

    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。谢谢!
D
DevDavid

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

ES5 答案

函数 removeDuplicates(arr, equals) { var originalArr = arr.slice(0);变量 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); } } } 函数 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"} ];删除重复项(事物,事物平等);控制台.log(东西);

原始 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);

两个对象不会评估相等,即使它们共享相同的属性和值。
是的,我知道。但公平地说,我没有正确阅读这个问题:我没有发现他需要清除的是具有相同属性的对象。我会编辑我的答案。
而不是 while inside arrayContains- 使用 Array.prototype..some 方法 如果数组成员之一匹配条件,则返回 true
h
h2ooooooo

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

“消除重复”功能通常称为 uniqueuniq。一些现有的实现可能会结合这两个步骤,例如,prototype's uniq

This post 几乎没有可以尝试的想法(有些想法要避免 :-))如果您的图书馆还没有!我个人认为这是最直接的:

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

这不适用于没有自然排序顺序的通用对象。
没错,我添加了用户提供的比较版本。
您的用户提供的比较版本将不起作用,因为如果您的比较函数是 function(_a,_b){return _a.a===_b.a && _a.b===_b.b;},那么数组将不会被排序。
那是一个无效的比较函数。 From developer.mozilla.org/en/Core_JavaScript_1.5_Reference/… ... function compare(a, b) { if (a is less than b by some ordering criteria) return -1;如果(根据排序标准,a 大于 b)返回 1; // a 必须等于 b return 0; } ...
d
doğukan

我认为最好的方法是使用 reduceMap object这是单行解决方案。

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' 是关键。 (别担心。我们将使用 .values() 方法仅获取值)'obj' 是整个对象。 */


通过删除回车和/或行之间的换行符,任何东西都可以成为“单行解决方案”:P。
c
chickens

一个带地图的班轮(高性能,不保留订单)

在数组 arr 中查找唯一的 id

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

如果订单很重要,请查看带有过滤器的解决方案:Solution with filter

数组 arr 中的多个属性(placename)唯一

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

多属性解决方案完美运行。非常感谢!
C
Cliff Hall

该死的,孩子们,让我们粉碎这件事,我们为什么不呢?

让 uniqIds = {}, source = [{id:'a'},{id:'b'},{id:'c'},{id:'b'},{id:'a'},{ id:'d'}];让过滤 = source.filter(obj => !uniqIds[obj.id] && (uniqIds[obj.id] = true));控制台.log(过滤); // 预期:[{id:'a'},{id:'b'},{id:'c'},{id:'d'}];


这没有回答原始问题,因为这是搜索 id。该问题需要整个对象在所有字段(例如 placename)中都是唯一的
这是对上述问题概括的改进。最初的问题是 9 年前发布的,所以最初的发布者今天可能并不担心 placename。阅读此线程的任何人都在寻找一种对对象列表进行重复数据删除的最佳方法,这是一种紧凑的方法。
S
Stevo

再添加一个到列表中。将 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')

P
Pragmateek

您还可以使用 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,很好,多解释一下 dedupThings 的内部工作会很好 - 从好的方面来说,我现在明白 reduce 了:D
很好的单行答案,最后我看到了 Map 的用法:D
佚名

TypeScript 解决方案

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

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

使用类型 any 完全违背了 TypeScript 的目的
当然,我认为这消除了税务编制者将进行的任何推断检查。
当你有对象时停止使用 stringify !天啊,这就是我不喜欢 JS 的原因,它让人们做各种丑陋的事情。
如果该数组包含任何具有循环引用的对象,则此代码将平铺在其表面。
l
leonheess

考虑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 }]

lodash 的 uniq 和 uniqBy 都没有做到这一点,但您的解决方案做到了。谢谢!但是,如果它是直接副本,请提供代码的来源。 lodash.com/docs/4.17.10#uniqWith
非常适合我这个解决方案
S
Savan Akbari

让 myData = [{place:"here",name:"stuff"}, {place:"there",name:"morestuff"}, {place:"there",name:"morestuff"}];让 q = [...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];控制台日志(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),第一个元素(键)将是对象的字符串化版本,第二个(值)将是一个对象本身。将上面创建的数组列表添加到 new Map() 将使键作为字符串化对象,并且任何相同的键添加都将导致覆盖已经存在的键。使用 .values() 将为 MapIterator 提供 Map 中的所有值(在我们的例子中为 obj )最后,传播 ... 运算符以提供具有上述步骤的值的新 Array。


ن
نور

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 { 返回东西; } }, []); console.log(filteredArr)

通过 Set 对象解决方案 |根据数据类型

常量看见 = 新的 Set(); const things = [ {place:"here",name:"stuff"}, {place:"there",name:"morestuff"}, {place:"there",name:"morestuff"} ]; const filtersArr = things.filter(el => { const duplicate = seen.has(el.place); seen.add(el.place); return !duplicate; }); console.log(filteredArr)

Set对象特征

Set Object 中的每个值都必须是唯一的,将检查值是否相等

Set 对象的目的是根据数据类型存储唯一值,无论是原始值还是对象引用。它有四个非常有用的实例方法 addclearhas & delete

独特和数据类型功能:..

add方法

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

has 方法

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

delete 方法

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

clear 方法

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

Set 对象也有 迭代方法 &更多功能..

从这里开始阅读: Set - JavaScript | MDN


l
leonheess

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

上面,我们使用了 && 的短路功能。如果 && 左侧的值为真,则返回 && 右侧的值。如果左侧为假,则返回 && 左侧的内容。

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


也许解释一下你的代码,以及它是如何回答这个问题的?
谢谢,mix3d。我添加了说明。
谢谢你解释这个!该解决方案对我有用,并且与此处发布的其他几个类似,尽管我不明白发生了什么:)
谢谢,整洁的解决方案
l
leonheess

这种方式对我很有效:

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
    }
  })
}

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


l
leonheess

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

您可以删除重复项,也可以使用此代码删除所有重复项。好的
M
Masih Jahangiri

为懒惰的 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));
} 

uniqueKey 应该是 keyof T 而不是 string 以使其更精确。
S
Sнаđошƒаӽ

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

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

考虑下面的数组。假设您想仅考虑 propOnepropTwo 来查找此数组中的唯一对象,并忽略可能存在的任何其他属性。

预期结果应仅包括第一个和最后一个对象。所以这里是代码:

const array = [{ propOne: 'a', propTwo: 'b', propThree: '我没有参与这个...' }, { propOne: 'a', propTwo: 'b', someOtherProperty: '没有人关心这个...' }, { propOne: 'x', propTwo: 'y', yetAnotherJunk: '我真的一文不值', noOneHasThis: '我有没有人有的东西' }]; const uniques = [...new Set(array.map(x => JSON.stringify(((o) => ({ propOne: o.propOne, propTwo: o.propTwo }))(x)))) ] .map(JSON.parse); console.log(唯一);


它可以工作,但其他属性将被清除,是否可以保留所选对象的其余属性?
@ThanwaCh。这是可行的,这实际上是一个偏好问题 - 只需要确定在重复的情况下应该从哪个对象中获取其余属性。使用我的示例,array 中的第一个和第二个对象成为 uniques 中的一个。现在该对象应该包含来自 array[0]propThree,还是来自 array[1]someOtherProperty,或两者兼有,还是其他?只要我们确切地知道在这种情况下该怎么做,您所要求的肯定是可行的。
这个解决方案非常适合我正在编码的用例。你能解释一下这部分是什么/做什么(({ propOne, propTwo }) => ({ propOne, propTwo }))(x)
@knot22 (x) 之前的部分是一个箭头函数,它将参数对象解包到属性 propOnepropTwo 中。了解对象解构here。现在又看了一遍代码,觉得应该写的清楚一点。我已经更新了代码。
Z
ZeroSum

另一种选择是创建一个自定义 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 package 作为轻量级对象比较器配对,以执行独特的数组过滤......例如不同的对象数组。只需换入 if (_.isEqual(a[i], b)) { 而不是查看 @ 单个属性
A
Afeesudheen

在这里,我找到了一个使用 reduce 方法从对象数组中删除重复项的简单解决方案。我正在根据对象的位置键过滤元素

const med = [ {name:'name1',位置:'left'},{name:'name2',位置:'right'},{name:'name3',位置:'left'},{name:' name4',位置:'right'},{name:'name5',位置:'left'},{name:'name6',位置:'left1'}] const arr = []; med.reduce((acc, curr) => { if(acc.indexOf(curr.position) === -1) { acc.push(curr.position); arr.push(curr); } 返回 acc; } , []) 控制台.log(arr)


工作完美。
L
Leonid Pyrlia

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

const things = [ {place:"here",name:"stuff"}, {place:"there",name:"morestuff"}, {place:"there",name:"morestuff"} ]; const filters = things.filter(function({place, name}) { const key =`${place}${name}`; return !this.has(key) && this.add(key); }, new Set );控制台.log(过滤);

但是,它不适用于箭头函数 () =>,因为 this 绑定到它们的词法范围。