2

The code to get an array without repeated items has become elegant since ES6:

[...new Set(array)];

That's it!

However, this is only removing duplicates if the array has elements with a primitive data type (string, boolean, number, ...).

What about a Set of object literals? How to make that work without getting duplicates, using a syntax close to the syntax used above?

var array=["aaa","bbb","aaa","cc","aaa","bbb"];
var out=[...new Set(array)];
console.log(out)

//----Literal Object 

array=[{n:"J",last:"B"},{n:"J",last:"B"}];
out=[...new Set(array)];
console.log(out)

The code above produces a set with 2 elements, yet I want it to only have one in this case.

I could use serialize/de-serialize methodology to achieve this:

[...new Set(array.map(
    //-- SERIALIZE:
    (e) => `${e.n}:${e.last}`
))].map(
    //-- DE-SERIALIZE:
    (e) => ({ n: `${e.split(':')[0]}`, last: `${e.split(':')[1]}` })
)

However, I am looking for an ES6 built-in.

3
  • 4
    Set will work fine for objects as well. However, in your case you have two different objects, even though they have the same properties (and values). You have define your own logic to compare and filter such arrays. Commented Jul 25, 2016 at 21:54
  • 4
    Set does not support custom comparators. So, If you don't want to reinvent the wheel, you'll have to use something like Lodash's uniqueBy Commented Jul 25, 2016 at 21:59
  • Yep; what others are saying. Set doesn't work like that. Commented Jul 26, 2016 at 0:21

1 Answer 1

1

In JavaScript two objects are different if they are not the same reference, even when they look the same:

var a = {};
var b = {};
console.log(a === b); // false
b = a;
console.log(a === b); // true

Sets work similarly: unless objects added to it are referring to the same thing, they will be distinct.

A Custom Set

One idea to make it work like you want, is to create your own flavour of Set, i.e. MySet, giving it all the methods and properties you need for it to work as a Set.

Then in its implementation you would keep a Map in its internals, where you give a key to everything you store in it. You could then make sure that objects that you consider the same, get the same key in that map, and so are only stored once.

A non-efficient, but straightforward way of doing that, is to use JSON.stringify(item) as key. This has its limitations (e.g. adding self-referencing objects will make JSON.stringify(item) give up), but for the rest it does the job.

We could make MySet to accept an additional argument: the function to invoke to get an item's key value. Given the above idea, we would give it the default value JSON.stringify.

Consider this implementation, with your testing code added to it:

// Implementation of special Set:
class MySet {
    constructor(values = [], keyFunc = JSON.stringify) {
        // Use a map to store the values 
        this._map = new Map();
        // Which function to use for generating an item's key
        this._keyFunc = keyFunc;
        // Add the initial values
        for (var value of [...values]) this.add(value);
    }
    get size() {
        return this._map.size;
    }
    add(item) {
        // Key items by the given function
        this._map.set(this._keyFunc(item), item);
    }
    has(item) {
        return this._map.has(this._keyFunc(item));
    }
    *[Symbol.iterator] () {
        for (var pair of this._map) {
            yield pair[1]; // return the item
        }
    }
    // etc...
}

// Test it:
array = [{n:"J",last:"B"}, {n:"J",last:"B"}];
out = [...new MySet(array)];
console.log(out);

As you can see, although two objects were added to the set, it has only stored one.

Sign up to request clarification or add additional context in comments.

2 Comments

Nice approach, but it will fail when adding two objects with different property order.
Indeed, then the two objects are not considered the same. Whether this is a failure, is open for interpretation ;-)

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.