Home > Mobile >  How to get all unique objects (with two values) in an array?
How to get all unique objects (with two values) in an array?

Time:12-08

I'm storing some coordinates in an array. It looks like this:

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

How can I filter this array so the objects are unique, meaning there are no duplicates of objects with same x and y value? Expected output should be:

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

I've seen some similar solutions, but they didn't really solve this problem. I started with the following function

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

but it only returns objects with different x values, so it just completely ommits y value.

CodePudding user response:

One idea is to use a Set, map the x & y into a string, and then deserialize the Set to have unique x,y's..

eg..

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);
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

We can use Array.reduce(), along with a Map to get the required result.

We'd add each item to the map, using the concatenated x and y values as keys, then return the values() to get de-duplicated values.

This will have complexity of O(n), so it will be efficient for large arrays.

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; }
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Or with a regular object:

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; }
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

A pretty inefficient (O(n^2)), but flexible and straightforward solution: You first define a function that checks if two coordinates are equal. Then you filter all elements which have an equal element at a later position in the array.

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))
<iframe name="sif4" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

You can use originalArray.reduce() with an array instead of an object, so you can make use of 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;
  }, [])
);
<iframe name="sif5" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

Yet another simple solution using a temporary array. However not the best as I could say:

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

CodePudding user response:

.sort the input first by x then by y. This will put duplicate items next to each other. Then .reduce the sorted array to keep unique items (this step requires only one pass):

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 sorted = coords.slice(); // don't sort the original array
sorted.sort((a, b) => a.x - b.x || a.y - b.y);

const output = sorted.reduce((arr, val) => {
  if (arr.length === 0 || arr[arr.length - 1].x !== val.x || arr[arr.length - 1].y !== val.y) {
    arr.push(val);
  }
  return arr;
}, []);

console.log(output);
<iframe name="sif6" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related