1

Does Javascript have a built-in type for making a set out of data-objects and arrays?

let set = new Set();
set.add({"language": "ecmascript"});
set.add({"language": "ecmascript"});
set.add({"language": "ecmascript"});
set.add({"language": "ecmascript"});
set.add([1,2,3]);
set.add([1,2,3]);
set.add([1,2,3]);
set.add([1,2,3]);
console.log(set);

The Set I'm using above is only useful for primitives.

7
  • what is the use case for this? Commented Apr 17, 2019 at 12:24
  • 1
    You could use the Set and convert the arrays and objects with JSON.stringify method and then back to their original form with JSON.parse. Commented Apr 17, 2019 at 12:24
  • The docs for Set include whether primitive values or object references., so please explain why Set is not useful for you here. Is the real problem keeping references to the object so you can compare two different objects with the same properties and values? Commented Apr 17, 2019 at 12:24
  • @Petros : That's a reasonable technique, but the property names would have to be in the exact same order for each serialization. Does JSON.stringify alphabetize them? Commented Apr 17, 2019 at 12:33
  • 2
    @Petros - That's quite fragile, because JSON.stringify follows the ES2015 property order, which is defined by when properties were added to the object. So {a:1,b:2} and {b:2,a:1} serialize to different JSON strings, per specfication. Commented Apr 17, 2019 at 12:35

1 Answer 1

2

The Set I'm using above is only useful for primitives.

That's incorrect, it works just fine for objects. The problem is that distinct objects with the same properties and property values are not equal, so doing set.add({"language": "ecmascript"}); twice adds two non-equal objects to the set (both with the same property name and value).

If you add the same object more than once, it won't be added a second time:

const set = new Set();
const obj = {"language": "ecmascript"};
set.add(obj);
set.add(obj);
console.log(set.size); // 1

Does Javascript have a built-in type for...

If you want objects with the same properties and values to be treated as equal, then no. You'd need to be able to specify a comparison operation, and there's no built-in Set in JavaScript that lets you define the comparison operation to use.

Obviously, you can create one. As a starting point, I'd probably use a Map keyed by the names of the properties on the object, sorted and turned into a string via JSON.stringify. (Although that won't work if you want to have Symbol keys as part of the definition of equality.) For instance, if you're only considering own properties:

const key = JSON.stringify(Object.getOwnPropertyNames(object).sort());

The value for an entry could be either just an array of the objects with those keys that you do a linear search on, or a second Map keyed by some kind of hash of the property values, depending on how many objects you need to handle...


In comments, I asked:

Do you only need to handle objects with JSON-serializable values?

and you answered:

I have a bunch of objects that are already serialized, but there are duplicates that I'd like to eliminate and then re-serialize.

Yeah, you can use a Set for that if you don't mind re-serializing, or a Map if you want to skip the re-serializing part:

const unique = new Map();
for (const source of serializedObjects) {
    const sourceObject = JSON.parse(source); // Or parse from whatever serialization it is
    // Build object adding properties in alpha order for stability
    const keyObj = {};
    for (const key of Object.keys(sourceObject).sort()) {
        keyObj[key] = sourceObject[key];
    }
    // Save it using JSON.stringify, which uses ES2015 property order
    map.set(JSON.stringify(keyObj), source);
}
const uniqueSourceStrings = [...map.values()];

Or for the de-serialized objects themselves:

const unique = new Map();
for (const source of serializedObjects) {
    const sourceObject = JSON.parse(source); // Or parse from whatever serialization it is
    // Build object adding properties in alpha order for stability
    const keyObj = {};
    for (const key of Object.keys(sourceObject).sort()) {
        keyObj[key] = sourceObject[key];
    }
    // Save it using JSON.stringify, which uses ES2015 property order
    map.set(JSON.stringify(keyObj), sourceObject); // <=================== changed
}
const uniqueSourceObject = [...map.values()];
//    ^^================================================================== changed
Sign up to request clarification or add additional context in comments.

5 Comments

BTW, I have your book on Pre-Order at Amazon (proof). I get notifications from time to time saying the release has been delayed. How's that coming along ?
...almost...there... :-) The publisher's latest release date revision is, I think, intentionally pessimistic. But speaking of the book, I'm off back to edit land. (Updated the answer, btw.) (The description is also out of date, they tell me they'll update it, but... The book covers ES2019.)
Well, I look forward to it. Stop being a perfectionist and release that thing. Let your readers report typos, and correct that stuff in the 2nd edition ;)
@LonnieBest - Left perfection behind some ways back. Shooting for adequate. ;-)
HA! Well, I'm certain it will be that.

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.