18

How can i use Set() in objects? I read the documentation, it works with values in array, but with objects it do not work. Why? I want to avoid duplicate items that has exacly the same values.

let nameList_one = [{ name: 'harry', id: 1 }, { name: 'john', id: 2 }, { 'name': 'trevor', id: 3  }]
let nameList_two = [{ name: 'harry', id: 1  }, { name: 'ron', id: 4  }, { name: 'jackson', id: 5  }]

let fullNameList = [...nameList_one , ...nameList_two ]

let filteredList = [...new Set(fullNameList)] // the output is the merge of two array, duplicating "harry".

Output:

[{ name: 'harry', id: 1 }, { name: 'john', id: 2 }, { 'name': 'trevor', id: 3 }, { name: 'harry', id: 1 }, { name: 'ron', id: 4 }, { name: 'jackson', id: 5 }]

Expected Output:

[{ name: 'harry', id: 1 }, { name: 'john', id: 2 }, { 'name': 'trevor', id: 3 }, { name: 'ron', id: 4 }, { name: 'jackson', id: 5 }]

Thanks!!

8
  • 4
    Two different objects will never be === to each other. Object comparison only involves the object's reference value; the property values don't matter. Commented Apr 13, 2018 at 15:17
  • You would probably need to prototype the function Set() or add a custom comparison method to your object type, as @Pointy said object comparison is done by reference which will not be equal. Commented Apr 13, 2018 at 15:18
  • 2
    Relevant info: How to customize object equality for Javascript Set - spoiler: "you can't" so you can't really use a Set for what you're trying to do. It simply doesn't do what you're looking for and can't be customized to do it either. Commented Apr 13, 2018 at 15:21
  • 2
    You can use reduce, something like fullNameList.reduce((a, c) => { !a.find(v => v.id === c.id) && a.push(c); return a; }, []); Commented Apr 13, 2018 at 15:25
  • 1
    You'll need to decide what makes two objects the same and use that for the comparison inside the find function. I assumed that is the id property. Commented Apr 13, 2018 at 15:28

5 Answers 5

31

This doesn't work with objects because they are not same objects:

{ name: 'harry', id: 1 } !== { name: 'harry', id: 1 }

A way that may work is:

let filteredList = [...new Set(fullNameList.map(JSON.stringify))].map(JSON.parse);

It cannot be recommended, because it uses serialization where it's unnecessary. Serialization will destroy object instances that cannot be serialized, will ignore non-enumerable properties and won't handle circular references. Objects that have different property order won't be removed as duplicates. Their properties should be sorted first. In case there are nested objects, keys should be sorted recursively. This makes this approach impractical.

If the objects are identified by one or several fields, Map with serialized fields as map keys can be used:

let filteredList = [
  ...new Map(fullNameList.map(obj => [`${obj.id}:${obj.name}`, obj]))
  .values()
];
Sign up to request clarification or add additional context in comments.

4 Comments

Converting an object to string representation. You're welcome.
we cannot use object to string representation because order of keys may be diffrent
@lalrishav I agree. I came across the same issue when I was trying to store stringified objects using Set data type in Redis. Same data returned for the same entity from Firebase was not equal when stringified just because the order of the properties was different.
@AlperGüven FWIW, the last time this problem occurred to me with Firebase, I used json-stable-stringify
8

A nice way of doing this is to subclass Set.

class UniqueNameSet extends Set {
    constructor(values) {
        super(values);

        const names = [];
        for (let value of this) {
            if (names.includes(value.name)) {
                this.delete(value);
            } else {
                names.push(value.name);
            }
        }
    }
}

This gives you all the other methods/properties of Set, but adds the extra functionality you are looking for.

Comments

3

Each object is a pointer to a memory address, so even if the two objects have the same properties it wont be equal. One way to do this would be using JSON.stringify and copare the stringified object.

Comments

0

To get unique field of array of object, if you have an idea of the field, you can try this.

const data = [{name: 'Bob'}, {name: 'John'}, {name:'Bob'}];

const res =  data.reduce((acc, curr, idx, arr)=> {
  if(acc[1].indexOf(curr.name)==-1) {
      acc[1].push(curr.name);
      acc[0].push(curr)
  }
  return acc
}, [[],[]])[0]

console.log(res)

 //[{ name: "Bob" }, Object { name: "John" }]

Comments

-2

FOR CLARITY

const array = [
  {
    name: "Joe",
    age: 17,
  },
  {
    name: "Joe",
    age: 17,
  },
  {
    name: "Carl",
    age: 35,
  }
];

let myArray = [];

for(let a = 0; a < array.length; a++){

   let string = JSON.stringify(array[a]);

   myArray.push(string)

};

const set = new Set(myArray);

let newArray = [];

set.forEach((element) => {

  const parsedData = JSON.parse(element);

  newArray.push(parsedData);

});

3 Comments

What are you trying to clarify?
Making it readable cause I saw one written on one line. Not all JS newbies will understand that
Consider adding formatting then, because the code written as plain text it is significantly less readable than formatted

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.