删除对象数组中的重复项 Javascript

IT技术 javascript arrays underscore.js
2021-02-21 02:38:29

我有一个对象数组

list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}]

我正在寻找一种有效的方法(如果可能的话O(log(n)))来删除重复项并最终得到

list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}]

我已经尝试过_.uniq甚至_.contains但找不到令人满意的解决方案。

谢谢!

编辑:该问题已被确定为另一个问题的重复。我在发布之前看到了这个问题,但它没有回答我的问题,因为它是一个对象数组(而不是一个二维数组,谢谢 Aaron),或者至少另一个问题的解决方案在我的情况下不起作用。

6个回答

纯 javascript (ES2015),使用 Set

const list = [{ x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }, { x: 1, y: 2 }];

const uniq = new Set(list.map(e => JSON.stringify(e)));

const res = Array.from(uniq).map(e => JSON.parse(e));

document.write(JSON.stringify(res));

@AminJafari 即使在 2020 年也没有使用 var 。只要注意范围,var 绝对没问题。虽然这个答案不是最高效的,但它按照 OP 的问题工作
2021-04-23 02:38:29
在 ES2015 中编写代码时不应该使用 var!
2021-04-28 02:38:29
@SeaWarrior404 我知道,这就是我投赞成票的原因:) 在可以使用 const 的地方使用 var 并不是一个好习惯
2021-05-05 02:38:29
stringify并且parse看起来不是实现这一目标的最高效方式。
2021-05-15 02:38:29

尝试使用以下方法:

list = list.filter((elem, index, self) => self.findIndex(
    (t) => {return (t.x === elem.x && t.y === elem.y)}) === index)
这是完美的。谢谢
2021-05-16 02:38:29
只是一个问题,你不能只Object.is()用于过滤器的比较吗? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-05-20 02:38:29

香草JS版本:

const list = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}];

function dedupe(arr) {
  return arr.reduce(function(p, c) {

    // create an identifying id from the object values
    var id = [c.x, c.y].join('|');

    // if the id is not found in the temp array
    // add the object to the output array
    // and add the key to the temp array
    if (p.temp.indexOf(id) === -1) {
      p.out.push(c);
      p.temp.push(id);
    }
    return p;

    // return the deduped array
  }, {
    temp: [],
    out: []
  }).out;
}

console.log(dedupe(list));

我会使用Arrayr.prototype.reduceArrayr.prototype.some方法与扩展运算符的组合

1. 明确的解决方案基于数组对象包含的完整知识。

list = list.reduce((r, i) => 
  !r.some(j => i.x === j.x && i.y === j.y) ? [...r, i] : r
, [])

这里我们对比较对象的结构有严格的限制:{x: N, y: M}. [{x:1, y:2}, {x:1, y:2, z:3}]将被过滤[{x:1, y:2}]

2. 通用解决方案,JSON.stringify()比较对象可以具有任意数量的任何属性。

list = list.reduce((r, i) => 
  !r.some(j => JSON.stringify(i) === JSON.stringify(j)) ? [...r, i] : r
, [])

这种方法对属性顺序有限制,因此[{x:1, y:2}, {y:2, x:1}]不会被过滤。

3. 通用解决方案,Object.keys()顺序无关紧要。

list = list.reduce((r, i) => 
  !r.some(j => !Object.keys(i).some(k => i[k] !== j[k])) ? [...r, i] : r
, [])

这种方法还有另一个限制:比较对象必须具有相同的键列表。所以[{x:1, y:2}, {x:1}]会尽管有明显的差异进行过滤。

4. 通用解决方案,Object.keys() + .length

list = list.reduce((r, i) => 
  !r.some(j => Object.keys(i).length === Object.keys(j).length 
    && !Object.keys(i).some(k => i[k] !== j[k])) ? [...r, i] : r
, [])

对于最后一种方法,对象通过键的数量、键本身和键值进行比较。

我创建了一个Plunker来玩它。

为通用解决方案竖起大拇指,正是我所需要的!
2021-04-23 02:38:29
您不需要在每次迭代时重新创建数组。你可以用r.push(i)if (!r.some(j => JSON.stringify(i) === JSON.stringify(j))) r.push(i); return r
2021-04-27 02:38:29
你能解释一下第四种方法发生了什么,特别是数组部分和一些比较?它有点难以理解
2021-04-29 02:38:29
@SeaWarrior404 第 4 种方法是键/值比较的组合,显示为第 3 页中的第二个“一些”,并且长度比较显示为第一个“一些”,它迭代地比较初始中每个对象的键的长度数组与初始数组中所有其他对象的键的长度,以确保列表中的所有项目具有相同数量的字段。如果给定对象与列表中的其他对象具有相同数量的字段,它将被发送到键值比较(第二个“一些”)。如果没有,它将立即被过滤掉。
2021-05-02 02:38:29

以下将起作用:

var a = [{x:1,y:2}, {x:3,y:4}, {x:5,y:6}, {x:1,y:2}];

var b = _.uniq(a, function(v) { 
    return v.x && v.y;
})

console.log(b);  // [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 } ]
除非您要处理 1000 个元素中的 10 个,否则性能应该不是问题。我想你必须权衡加载一个单独的库的成本(如果 lodash 已经不在你的堆栈中)或者只是使用稍微更冗长的 vanilla 函数。
2021-04-21 02:38:29
谢谢!对我有用,它比安迪的解决方案快吗?
2021-04-22 02:38:29
我不知道。编写的代码较少,下划线似乎包含在任何一种方式中......@kwn
2021-05-09 02:38:29
不要认为这可以正常工作,例如 1 && 2 == 2 && 2
2021-05-12 02:38:29
如果输入是 [{x:1, y: 2}, {x: 2. y:2}] 那么结果将是 [{x:1, y:2}] - 第二个对象将被删除,即使这不是重复的
2021-05-20 02:38:29