如何获取数组中所有具有两个值的唯一对象?

3

我正在一个数组中存储一些坐标,它长这样:

const coords = [{x: 260, y: 60}, {x: 180, y: 0}, {x: 180, y: 240}, {x: 360, y: 120}, {x: 180, y: 60}, {x: 180, y: 60}, {x: 180, y: 60}]

如何过滤这个数组,使得对象是唯一的,即没有具有相同x和y值的重复对象?预期输出应该是:
const coords = [{x: 260, y: 60}, {x: 180, y: 0}, {x: 180, y: 240}, {x: 360, y: 120}, {x: 180, y: 60}]

我见过一些类似的解决方案,但他们并没有真正解决这个问题。 我从以下函数开始:

const output = Object.values(
  coords.reduce( (c, e) => {
    if (!c[e.x]) c[e.x] = e;
    return c;
  }, {})

但它只返回具有不同x值的对象,因此完全忽略了y值。


1
你明白这个 reduce 调用是如何工作的吗?你的哈希键是 e.x,所以它只基于 x 进行去重。你的哈希键不包括 y,那为什么不直接包含 y 呢?例如,将键设置为 \${e.x}-${e.y}`。顺便说一下,将来可以使用 [Records](//github.com/tc39/proposal-record-tuple) 来实现这个功能:new Set([ #{ x: 260, y: 60 }, #{ x: 180, y: 0 },])`。 - Sebastian Simon
5个回答

3
一个想法是使用 Set,将 x 和 y 映射成一个字符串,然后反序列化 Set 来获得唯一的 x,y 值。
例如:

const coords = [{x: 260, y: 60}, {x: 180, y: 0}, {x: 180, y: 240}, {x: 360, y: 120}, {x: 180, y: 60}, {x: 180, y: 60}, {x: 180, y: 60}];

const dedup = [...new Set(coords.map(m => `${m.x}:${m.y}`))].map(m => {
  const [x,y] = m.split(':').map(n => n | 0);
  return {x,y};
});

console.log(dedup);


1
我们可以使用 Array.reduce(),再加上一个Map来获得需要的结果。
我们将每个项目添加到 Map 中,使用连接的 x 和 y 值作为键,然后返回 values() 以获取去重后的值。
这将具有 O(n) 的复杂度,因此对于大型数组来说效率很高。

const coords = [{x: 260, y: 60}, {x: 180, y: 0}, {x: 180, y: 240}, {x: 360, y: 120}, {x: 180, y: 60}, {x: 180, y: 60}, {x: 180, y: 60}];

const dedup = [...coords.reduce((map, { x, y }) => {
   return (map.set(`${x}-${y}`, { x, y }));
}, new Map()).values()];

console.log('De-duplicated:', dedup)
     
.as-console-wrapper { max-height: 100% !important; top: 0; }

或者使用普通物体:

const coords = [{x: 260, y: 60}, {x: 180, y: 0}, {x: 180, y: 240}, {x: 360, y: 120}, {x: 180, y: 60}, {x: 180, y: 60}, {x: 180, y: 60}];

const dedup = Object.values(coords.reduce((acc, { x, y }) => { 
    return { ...acc, [`${x}-${y}`]: { x, y }}
}, {}));

console.log('De-duplicated:', dedup)
.as-console-wrapper { max-height: 100% !important; top: 0; }


0
一个相当低效(O(n^2))但灵活且直接的解决方案:首先定义一个函数来检查两个坐标是否相等。然后过滤掉所有在数组中后面位置有相等元素的元素。

const coords = [{x: 260, y: 60}, {x: 180, y: 0}, {x: 180, y: 240}, {x: 360, y: 120}, {x: 180, y: 60}, {x: 180, y: 60}, {x: 180, y: 60}]

const customUnique = (arr, isEqual) => {
  // filter elements where an equal element exists at an earlier position
  // thus the first element is kept
  return arr.filter((a, i) => !arr.some((b, j) => i > j && isEqual(a, b)))
}

console.log(customUnique(coords, (a, b) => a.x === b.x && a.y === b.y))


0
你可以使用 originalArray.reduce() 与数组一同使用,而不是对象,这样你就可以利用 array.find

const coords = [{x: 260, y: 60}, {x: 180, y: 0}, {x: 180, y: 240}, {x: 360, y: 120}, {x: 180, y: 60}, {x: 180, y: 60}, {x: 180, y: 60}]

console.log(
  coords.reduce((arr, e) => {
    if (!arr.find(item => item.x == e.x && item.y == e.y)) {
      arr.push(e);
    }
    return arr;
  }, [])
);


这是一个非常有趣的解决方案。 - Abgar

0

另一种使用临时数组的简单解决方案。然而不是我能说的最好的。

const filteredCoords: any = [];

for(let coord of coords)
  if (!filteredCoords.find((ele: { x: number; y: number; }) => ele.x == coord.x && ele.y == coord.y)){
    filteredCoords.push(coord)
}

但相当容易理解。谢谢,伙计! - Abgar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接