Update 3/2022

There is currently a proposal to add Records and Tuples (basically immutable Objects and Arrays) to Javascript. In that proposal, it offers direct comparison of Records and Tuples using === or !== where it compares values, not just object references AND relevant to this answer both Set and Map objects would use the value of the Record or Tuple in key comparisons/lookups which would solve what is being asked for here.

Since the Records and Tuples are immutable (can't be modified) and because they are easily compared by value (by their contents, not just their object reference), it allows Maps and Sets to use object contents as keys and the proposed spec explicitly names this feature for Sets and Maps.

This original question asked for customizability of a Set comparison in order to support deep object comparison. This doesn't propose customizability of the Set comparison, but it directly supports deep object comparison if you use the new Record or a Tuple instead of an Object or an Array and thus would solve the original problem here.

Note, this proposal advanced to Stage 2 in mid-2021. It has been moving forward recently, but is certainly not done.

Mozilla work on this new proposal can be tracked here.

Official Spec Draft here.

Incomplete polyfill here.

Note the polyfill will never be a complete polyfill because the spec uses new language features and implements new types in the language. But, the polyfill can be used with some work-arounds.


Original Answer

The ES6 Set object does not have any compare methods or custom compare extensibility.

The .has(), .add() and .delete() methods work only off it being the same actual object or same value for a primitive and don't have a means to plug into or replace just that logic.

You could presumably derive your own object from a Set and replace .has(), .add() and .delete() methods with something that did a deep object comparison first to find if the item is already in the Set, but the performance would likely not be good since the underlying Set object would not be helping at all. You'd probably have to just do a brute force iteration through all existing objects to find a match using your own custom compare before calling the original .add().

Here's some info from this article and discussion of ES6 features:

5.2 Why can’t I configure how maps and sets compare keys and values?

Question: It would be nice if there were a way to configure what map keys and what set elements are considered equal. Why isn’t there?

Answer: That feature has been postponed, as it is difficult to implement properly and efficiently. One option is to hand callbacks to collections that specify equality.

Another option, available in Java, is to specify equality via a method that object implement (equals() in Java). However, this approach is problematic for mutable objects: In general, if an object changes, its “location” inside a collection has to change, as well. But that’s not what happens in Java. JavaScript will probably go the safer route of only enabling comparison by value for special immutable objects (so-called value objects). Comparison by value means that two values are considered equal if their contents are equal. Primitive values are compared by value in JavaScript.

Answer from jfriend00 on Stack Overflow
Top answer
1 of 12
162

Update 3/2022

There is currently a proposal to add Records and Tuples (basically immutable Objects and Arrays) to Javascript. In that proposal, it offers direct comparison of Records and Tuples using === or !== where it compares values, not just object references AND relevant to this answer both Set and Map objects would use the value of the Record or Tuple in key comparisons/lookups which would solve what is being asked for here.

Since the Records and Tuples are immutable (can't be modified) and because they are easily compared by value (by their contents, not just their object reference), it allows Maps and Sets to use object contents as keys and the proposed spec explicitly names this feature for Sets and Maps.

This original question asked for customizability of a Set comparison in order to support deep object comparison. This doesn't propose customizability of the Set comparison, but it directly supports deep object comparison if you use the new Record or a Tuple instead of an Object or an Array and thus would solve the original problem here.

Note, this proposal advanced to Stage 2 in mid-2021. It has been moving forward recently, but is certainly not done.

Mozilla work on this new proposal can be tracked here.

Official Spec Draft here.

Incomplete polyfill here.

Note the polyfill will never be a complete polyfill because the spec uses new language features and implements new types in the language. But, the polyfill can be used with some work-arounds.


Original Answer

The ES6 Set object does not have any compare methods or custom compare extensibility.

The .has(), .add() and .delete() methods work only off it being the same actual object or same value for a primitive and don't have a means to plug into or replace just that logic.

You could presumably derive your own object from a Set and replace .has(), .add() and .delete() methods with something that did a deep object comparison first to find if the item is already in the Set, but the performance would likely not be good since the underlying Set object would not be helping at all. You'd probably have to just do a brute force iteration through all existing objects to find a match using your own custom compare before calling the original .add().

Here's some info from this article and discussion of ES6 features:

5.2 Why can’t I configure how maps and sets compare keys and values?

Question: It would be nice if there were a way to configure what map keys and what set elements are considered equal. Why isn’t there?

Answer: That feature has been postponed, as it is difficult to implement properly and efficiently. One option is to hand callbacks to collections that specify equality.

Another option, available in Java, is to specify equality via a method that object implement (equals() in Java). However, this approach is problematic for mutable objects: In general, if an object changes, its “location” inside a collection has to change, as well. But that’s not what happens in Java. JavaScript will probably go the safer route of only enabling comparison by value for special immutable objects (so-called value objects). Comparison by value means that two values are considered equal if their contents are equal. Primitive values are compared by value in JavaScript.

2 of 12
37

As mentioned in jfriend00's answer customization of equality relation is probably not possible.

Following code presents an outline of computationally efficient (but memory expensive) workaround:

class GeneralSet {

    constructor() {
        this.map = new Map();
        this[Symbol.iterator] = this.values;
    }

    add(item) {
        this.map.set(item.toIdString(), item);
    }

    values() {
        return this.map.values();
    }

    delete(item) {
        return this.map.delete(item.toIdString());
    }

    // ...
}

Each inserted element has to implement toIdString() method that returns string. Two objects are considered equal if and only if their toIdString methods returns same value.

🌐
Medium
medium.com › @conboys111 › javascript-sets-how-does-the-equality-check-work-1c193c6ef047
JavaScript Sets: How Does the Equality Check Work? | by myHotTake | Medium
November 5, 2024 - This shows how I’m really focused on the strict identity of things in JavaScript’s Set. Numbers and strings are treated by their exact values, while objects (even if they seem identical) are treated by their unique identity in memory. So, back at “Club Set,” I, the bouncer, use strict equality (===) to check if people (or values) are already inside.
Discussions

Comparing sets for equality
Watch out: > new Set(['a']).difference(new Set(['a', 'b'])).size 0 You want .symmetricDifference() More on reddit.com
🌐 r/learnjavascript
3
1
July 24, 2024
how compare 2 Set ?
Because Sets are asign-by-reference: const set1 = new Set([7,2,4,7]) const set2 = set1 console.log(set1 === set2) //=> true You can test the equality of two Sets like this: const areSetsEqual = (setA, setB) => setA.size === setB.size && [...setA].every(value => setB.has(value)) areSetsEqual(set1, set2) //=> true More on reddit.com
🌐 r/learnjavascript
3
1
July 10, 2023
javascript - Implementing a set that treats object equality on value not reference - Stack Overflow
See How to determine equality for ... in JavaScript. ... Putting aside the deep equality problem for a sec, it would be nice to have a set constructed with an optional predicate for uniqueness. The predicate would take two args and return true if they should be considered equal. You'd have an O(n) insertion, but but a nice general purpose tool. ... Assuming the provided objects don't contain values that cannot be stringified to JSON (function, undefined, ... More on stackoverflow.com
🌐 stackoverflow.com
Don't Make Javascript Equality Look Worse Than It Is
If there's one thing we've learned this week, it's that Brendan Eich doesn't care about equality. More on reddit.com
🌐 r/programming
505
662
March 28, 2014
🌐
Reddit
reddit.com › r/learnjavascript › comparing sets for equality
r/learnjavascript on Reddit: Comparing sets for equality
July 24, 2024 -

I'm trying to compare two sets for equality and came across the difference() method of the Set object on MDN with the following example:

const odds = new Set([1, 3, 5, 7, 9]);

const squares = new Set([1, 4, 9]);

console.log(odds.difference(squares)); // Set(3) { 3, 5, 7 }

I'm trying to compare Set a and b in a function by returning a.difference(b).size === 0. However, I'm getting an error that says a.difference(b) is not a function, can anyone explain why this is happening?

Stackoverflow had similar posts where the solution was:

const a = new Set([1,2,3]);
const b = new Set([1,3,2]);

const areSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));

console.log(areSetsEqual(a,b))
🌐
GreatFrontEnd
greatfrontend.com › questions › quiz › how-do-sets-and-maps-handle-equality-checks-for-objects
How do `Set`s and `Map`s handle equality checks for objects? | Quiz Interview Questions with Solutions
September 5, 2021 - Sets and Maps in JavaScript handle equality checks for objects based on identity, not deep equality. Two object values are considered equal only when they refer to the same object.
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Global_Objects › Set
Set - JavaScript - MDN Web Docs
August 13, 2026 - This means NaN is considered the same as NaN (even though NaN !== NaN) and all other values are considered equal according to the semantics of the === operator. Also, for object keys, equality is based on object identity. They are compared by reference, not by value. See Using the Set object for examples.
🌐
Mozilla
developer.mozilla.org › en-US › docs › Web › JavaScript › Guide › Equality_comparisons_and_sameness
Equality comparisons and sameness - JavaScript | MDN
3 weeks ago - Same-value-zero equality is not ... custom code: ... function sameValueZero(x, y) { if (typeof x === "number" && typeof y === "number") { // x and y are equal (may be -0 and 0) or they are both NaN return x === y || (x !== x && y !== y); } return x === y; }...
Find elsewhere
🌐
GitHub
gist.github.com › 96f353516aaec57aa4e4
JavaScript Set equality checker · GitHub
JavaScript Set equality checker · Raw · eqset.js · This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
🌐
Adripofjavascript
adripofjavascript.com › blog › drips › object-equality-in-javascript.html
Object Equality in JavaScript - A Drip of JavaScript
Here is a very basic approach to checking an object's "value equality". 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 ...
🌐
Jcoglan
jsclass.jcoglan.com › equality.html
Equality methods and hashcodes - jsclass
Set uses equals() to make sure elements are unique, falling back to === if the elements do not implement equals(). jsclass does not modify built-in JavaScript classes, but the above classes do contain methods for comparing Array and Object instances.
🌐
GitHub
gist.github.com › rsms › 2d01d7f9f5159a7a7d1f
ES6 Set equality test · GitHub
April 10, 2017 - var s1 = new Set([1, 2]); var s2 = new Set([1, 2, 3]); assert(seteq(s1, s2) === false) // Fails miserably
🌐
Medium
medium.com › geekculture › object-equality-in-javascript-2571f609386e
Object Equality in JavaScript
August 15, 2021 - To check the instance you can use === equality, but to match the value you need to work more 😂 · let name = { firstName: "suprabha", lastName: "supi" }let fullName = { firstName: "suprabha", lastName: "supi" }function isEqual(obj1, obj2) ...
🌐
Dmitri Pavlutin
dmitripavlutin.com › how-to-compare-objects-in-javascript
How to Compare Objects in JavaScript - Dmitri Pavlutin
January 28, 2023 - The deep equality function correctly determines that hero1 and hero2 have the same properties and values, including the equality of the nested objects hero1.address and hero2.address.
Top answer
1 of 1
2

Assuming the provided objects don't contain values that cannot be stringified to JSON (function, undefined, symbol, etc.) You can use JSON.stringify().

One problem you might encounter is that stringifying { a: 1, b: 2 } doesn't produce the same result as { b: 2, a: 1 }. A fairly easy way to solve this would be to stringify the object and make sure the resulting JSON has properties placed in alphabetical order.

For this we can look to the answer provided in sort object properties and JSON.stringify.

I also think you are over complicating things by only stringifying values if they are an object. Instead you could just stringfy everything, null would result in "null", "string" would result in '"string"', etc. This simplifies the code by a lot. The only restriction then becomes that all values must be a valid JSON value.

// see linked answer
function JSONstringifyOrder(obj, space)
{
    const allKeys = new Set();
    JSON.stringify(obj, (key, value) => (allKeys.add(key), value));
    return JSON.stringify(obj, Array.from(allKeys).sort(), space);
}

class MySet extends Set {
    // The constructor makes uses of add(), so we don't need
    // to override the constructor.

    has(item) {
        return super.has(JSONstringifyOrder(item));
    }
    
    add(item) {
        return super.add(JSONstringifyOrder(item));
    }
    
    delete(item) {
        return super.delete(JSONstringifyOrder(item));
    }
}


const set = new MySet([[1,2,3], [1,2,3], 1, 2, 1]);
set.add([1,2,3]);
set.add({ a: { s: 1, d: 2 }, f: 3 });
set.add({ f: 3, a: { d: 2, s: 1 } });

// Stack Overflow snippets cannot print Set instances to the console
console.log(Array.from(set));
// or unserialized
Array.from(set, json => JSON.parse(json)).forEach(item => console.log(item));

🌐
Effect
effect.website › docs › v3 › trait › equal
Equal
In Effect it’s advisable to stop using JavaScript’s === and == operators and instead rely on the Equal.equals function. This function can work with any data type that implements the Equal interface.
🌐
Medium
hohanga.medium.com › how-to-compare-es6-sets-for-equality-f4bd15530114
How to Compare ES6 Sets for Equality? | by John Au-Yeung | Medium
March 18, 2023 - In the function, we compare is as and bs have different sizes. If they don’t then we return false . Then if they have the same size, we loop through the as set with the for-of loop.
🌐
xjavascript
xjavascript.com › blog › how-to-customize-object-equality-for-javascript-set
How to Customize Object Equality in JavaScript Set for Deep Comparisons (ES6 Guide) — xjavascript.com
JavaScript’s `Set` object, introduced in ES6, is a powerful data structure for storing unique values. By default, `Set` uses strict equality (`===`) to determine uniqueness, which works well for primitive values (strings, numbers, booleans).
🌐
SmartBear
support.smartbear.com › testcomplete › docs › reference › language › javascript › equal-method.html
equal Function | TestComplete Documentation
equal(2, 2) // true equal(2, 2.0) // true equal(2, "2") // true equal(1, true) // true equal(0, false) // true equal(0, null) // false equal(0, undefined) // false equal(null, undefined) // true · strictEqual Function JavaScript - Specifics of Usage
🌐
Jasmine
jasmine.github.io › tutorials › custom_equality
Custom Equality Testers
Otherwise, it should return undefined, to tell jasmine’s equality tester that it can’t compare the items. function myCustomTester(first, second) { if (typeof first === 'string' && typeof second === 'string') { return first[0] === second[1]; } }