Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove duplicates in an object array Javascript

I have an array of objects

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

And I'm looking for an efficient way (if possible O(log(n))) to remove duplicates and to end up with

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

I've tried _.uniq or even _.contains but couldn't find a satisfying solution.

Thanks!

Edit : The question has been identified as a duplicate of another one. I saw this question before posting but it didn't answer my question since it's an array of object (and not a 2-dim array, thanks Aaron), or at least the solutions on the other question weren't working in my case.

like image 850
kwn Avatar asked Mar 16 '16 09:03

kwn


People also ask

How do you remove duplicates in an object array?

To remove the duplicates from an array of objects:Use the Array. filter() method to filter the array of objects. Only include objects with unique IDs in the new array.

How do you remove duplicate array elements in JavaScript?

Use the filter() method: The filter() method creates a new array of elements that pass the condition we provide. It will include only those elements for which true is returned. We can remove duplicate values from the array by simply adjusting our condition.

How do you remove duplicates from an array of arrays?

To remove duplicates from an array: First, convert an array of duplicates to a Set . The new Set will implicitly remove duplicate elements. Then, convert the set back to an array.

How do you find duplicate objects in an array?

To check if there were duplicate items in the original array, just compare the length of both arrays: const numbers = [1, 2, 3, 2, 4, 5, 5, 6]; const unique = Array. from(new Set(numbers)); if(numbers. length === unique.


7 Answers

Plain javascript (ES2015), using 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));
like image 109
isvforall Avatar answered Oct 04 '22 19:10

isvforall


Try using the following:

list = list.filter((elem, index, self) => self.findIndex(
    (t) => {return (t.x === elem.x && t.y === elem.y)}) === index)
like image 44
Lava kumar N R Avatar answered Oct 04 '22 18:10

Lava kumar N R


Vanilla JS version:

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));
like image 26
Andy Avatar answered Oct 04 '22 20:10

Andy


I would use a combination of Arrayr.prototype.reduce and Arrayr.prototype.some methods with spread operator.

1. Explicit solution. Based on complete knowledge of the array object contains.

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

Here we have strict limitation on compared objects structure: {x: N, y: M}. And [{x:1, y:2}, {x:1, y:2, z:3}] will be filtered to [{x:1, y:2}].

2. Generic solution, JSON.stringify(). The compared objects could have any number of any properties.

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

This approach has a limitation on properties order, so [{x:1, y:2}, {y:2, x:1}] won't be filtered.

3. Generic solution, Object.keys(). The order doesn't matter.

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

This approach has another limitation: compared objects must have the same list of keys. So [{x:1, y:2}, {x:1}] would be filtered despite the obvious difference.

4. Generic solution, 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
, [])

With the last approach objects are being compared by the number of keys, by keys itself and by key values.

I created a Plunker to play with it.

like image 29
dhilt Avatar answered Oct 04 '22 19:10

dhilt


One liners for ES6+

If you want to find uniq by x and y:

arr.filter((v,i,a)=>a.findIndex(t=>(t.x === v.x && t.y===v.y))===i)

If you want to find uniques by all properties:

arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i)
like image 33
chickens Avatar answered Oct 04 '22 18:10

chickens


The following will work:

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 } ]
like image 36
baao Avatar answered Oct 04 '22 18:10

baao


Filter the array after checking if already in a temorary object in O(n).

var list = [{ x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }, { x: 1, y: 2 }],
    filtered = function (array) {
        var o = {};
        return array.filter(function (a) {
            var k = a.x + '|' + a.y;
            if (!o[k]) {
                o[k] = true;
                return true;
            }
        });
    }(list);

document.write('<pre>' + JSON.stringify(filtered, 0, 4) + '</pre>');
like image 36
Nina Scholz Avatar answered Oct 04 '22 19:10

Nina Scholz