7

I want to get array having unique objects.

Say I have array of objects

[{"a":"b"},{"c":"d"},{"a":"b"}] 

and I want unique value of array i.e

[{"a":"b"},{"c":"d"}]

is there any simplest way to do this.

Harshal_m_joshi
  • 1,489
  • 1
  • 16
  • 27
  • Yes, probably there is. – Matteo Tassinari Jun 03 '13 at 09:21
  • 1
    There's no simple way of doing it - you'll need to do it by hand. Start by writing a comparator function, which takes 2 objects and returns true/false based on whether the objects are equal. Then loop over the array with this comparator. – Aleks G Jun 03 '13 at 09:21
  • 1
    It is not a dupe, there you had a fixed property that you could lookup, here you don't. – BrunoLM Jun 03 '13 at 09:26
  • 1
    @harsha This is not the same question. In your linked question, the duplicity is checked by one specific key. Here the set of properties is not fixed for any of the objects and all of them must be equal. – Aleks G Jun 03 '13 at 09:28
  • Sorry guys,my bad,posted the wrong link.Will remove it. – Harsha Venkataramu Jun 03 '13 at 09:29

2 Answers2

7

If the array containing the same objects over and over again you can do make a function like this:

var oa = {"a":"b"},
    ob = {"c":"d"};

var array = [oa, ob, oa];

function unique(a) {
    var arr = [];
    for(var i = 0; i < a.length; i++) {
        if( !arr.indexOf(a[i]) == -1 ) {
            arr.push(a[i]);
        }
    }
    return arr; 
}

But this will most likely not work because the object even when they seams the same is different:

alert( {a: 1} === {a: 1} ); // false

But:

var a = {a: 1};
alert( a === a ); // true

And even this will be true:

var a = {a: 1},
    b = a;

alert( a === b ); // true

So you will have to test for that as well (this is a shallow caparation. One level objects):

function isEqual(a, b) {
    var prop;
    for( prop in a ) {
        if ( a[prop] !== b[prop] ) return false;
    }
    for( prop in b ) {
        if ( b[prop] !== a[prop] ) return false;
    }
    return true;
}

And we have to rewrite our unique function as well:

function unique(a) {
    var isAdded,
        arr = [];
    for(var i = 0; i < a.length; i++) {
        isAdded = arr.some(function(v) {
            return isEqual(v, a[i]);
        });
        if( !isAdded ) {
            arr.push(a[i]);
        }
    }
    return arr; 
}

Full Code:

var a = [{"a":"b"},{"c":"d"},{"a":"b"}],
    b = unique(a); // [{"a":"b"},{"c":"d"}]

function unique(a) {
    var isAdded,
        arr = [];
    for(var i = 0; i < a.length; i++) {
        isAdded = arr.some(function(v) {
            return isEqual(v, a[i]);
        });
        if( !isAdded ) {
            arr.push(a[i]);
        }
    }
    return arr; 
}
function isEqual(a, b) {
    var prop;
    for( prop in a ) {
        if ( a[prop] !== b[prop] ) return false;
    }
    for( prop in b ) {
        if ( b[prop] !== a[prop] ) return false;
    }
    return true;
}

Note the use of some methods is depending on ECMAScript 5th Edition:

Array.some
Array.indexOf

Andreas Louv
  • 46,145
  • 13
  • 104
  • 123
5

The simplest option is to compare objects by their JSON representation:

uniq = function(xs) {
    var seen = {};
    return xs.filter(function(x) {
        var key = JSON.stringify(x);
        return !(key in seen) && (seen[key] = x);
    });
}

For example:

console.log(
    uniq([{"a":"b"},{"c":"d"},{"a":"b"},{"a":"b"}])
)

// [{"a":"b"},{"c":"d"}]

Also, I recommend underscore.js for this kind of stuff, see Check if duplicate array pairs exist using underscore only for more discussion and examples.

Some commenters raised a concern that JSON.stringify is inadequate when comparing objects that differ only by the order of keys. I guess that comes down to the definition of "equality": {a:1,b:2} and {b:2,a:1} might be considered equal in one context and different in another. Still, if you want such objects to be "equal", you can extend JSON.stringify to be something like this:

toSortedJSON = function(obj) {
    return JSON.stringify(
        typeof obj == "object" ?
            Object.keys(obj).sort().reduce(function(o, key) {
                return o[key] = toSortedJSON(obj[key]), o;
            }, {}) : obj
    );
}

Then modify uniq to accept the key function:

uniq = function(xs, key) {
    var seen = {};
    return xs.filter(function(x) {
        var k = (key || JSON.stringify)(x);
        return !(k in seen) && (seen[k] = 1);
    });
}

and, finally pass the custom serializer to uniq:

console.log(
    uniq([
        {"a":1, "b":2},
        {"x":33},
        {"b":2, "a":1},
    ], toSortedJSON)
)

// [{"a":1,"b":2},{"x":33}]
Community
  • 1
  • 1
georg
  • 211,518
  • 52
  • 313
  • 390