2

How can I prevent a Set object from being populated with duplicate objects?

  [    
    {
        prop1: [{value: "val", disabled: true}] (1)
        prop2: [{value: [4, 5], disabled: true}] (1)
        prop3: [{value: "someOtherValue", disabled: true}] (1)
    },

    {
        prop1: [{value: "val", disabled: true}] (1)
        prop2: [{value: [4, 5], disabled: true}] (1)
        prop3: [{value: "someOtherValue", disabled: true}] (1)
    },
    {
        prop1: [{value: "otherValue", disabled: true}] (1)
        prop2: [{value: [3], disabled: true}] (1)
        prop3: [{value: "", disabled: true}] (1)
    },
  ]

So while looping through the array I check if the Set object contains a duplicate, but even if it does, check returns false all the time.

let s = new Set();

//for loop starts here

let check = s.has(obj) // false

if(check == false){
    s.add(obj);
}
Ondrej Slinták
  • 31,386
  • 20
  • 94
  • 126
Ciprian
  • 3,066
  • 9
  • 62
  • 98
  • 2
    That's because the objects have different references. They are fundamentally different (as compared via `===`). You'd have to implement your own checking algorithm – Andrew Li Jul 13 '18 at 04:16
  • @Li357 Even if objects with `==` it will return false because as you said they have difference references. `==` & `===` for objects will only return `true` if both have same references. – Karan Jul 13 '18 at 04:54

2 Answers2

4

Objects are passed by reference, which means that when you add them to a set, even if they are exactly the same, they will not be the same if you check them with ===.

var a = {val1: 'hello', val2: 'there'}
var b = {val1: 'hello', val2: 'there'}
console.log(a === b) // false
console.log(a == b)  // false

To solve this problem, you could write something like the following, taken from this article.

var a = {val1: 'hello', val2: 'there'};
var b = {val1: 'hello', val2: 'there'};

function isEquivalent(a, b) {
  // Create arrays of property names
  var aProps = Object.getOwnPropertyNames(a);
  var bProps = Object.getOwnPropertyNames(b);

  // If number of properties is different,
  // objects are not equivalent
  if (aProps.length != bProps.length) {
    return false;
  }

  for (var i = 0; i < aProps.length; i++) {
    var propName = aProps[i];

    // If values of same property are not equal,
    // objects are not equivalent
    if (a[propName] !== b[propName]) {
      return false;
    }
  }

  // If we made it this far, objects
  // are considered equivalent
  return true;
}

console.log(a === b); // false
console.log(a == b);  // false
console.log(isEquivalent(a, b)); // true

Using this algorithm will check the actual values of the objects rather than the references.

console.log(isEquivalent(a, b)) // true
Karan
  • 12,059
  • 3
  • 24
  • 40
Ben Botvinick
  • 2,837
  • 3
  • 16
  • 41
3

Perhaps a shorter--and admittedly lazier / less recommended--alternative to Ben's answer would be using JSON to make the comparison:

let s = new Set()

arr.forEach(obj => (
    !s.has(JSON.stringify(obj)) && s.add(JSON.stringify(obj))
))

s = new Set([...s].map(o => JSON.parse(o)))

It's worth mentioning that this will only work if the key-value pairs are in the same order.

fsl
  • 3,250
  • 1
  • 10
  • 20