views:

362

answers:

3

I have an object that contains an array of objects.

things = new Object();

things.thing = new Array();

things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});

I'm wondering what is the best method to remove duplicate objects from an array. So for example, things.thing would become...

{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}

Thanks in advance

A: 

Let's see ... a primitive one would be:

var arr = {};

for ( i=0; i < things.thing.length; i++ )
    arr[things.thing[i]['id']] = things.thing[i];

things.thing = new Array();
for ( key in arr )
    things.thing.push(arr[key]);

Ok, I think that should do the trick. Check it out, Travis.

aefxx
Clever. And it works. Thanks!
Travis
It does? Where are these 'id' properties coming from?
Tim Down
My bad - I edited my original question and changed the property 'id' to 'place' as there was some confusion about whether or not id was iterative or associative.
Travis
A: 

UPDATED

I've now read the question properly. This is a generic way of doing this: you pass in a function that tests whether two elements of an array are considered equal. In this case, it compares the values of the name and place properties of the two objects being compared.

function arrayContains(arr, val, equals) {
    var i = arr.length;
    while (i--) {
        if ( equals(arr[i], val) ) {
            return true;
        }
    }
    return false;
}

function removeDuplicates(arr, equals) {
    var originalArr = arr.slice(0);
    var i, len, j, val;
    arr.length = 0;

    for (i = 0, len = originalArr.length; i < len; ++i) {
        val = originalArr[i];
        if (!arrayContains(arr, val, equals)) {
            arr.push(val);
        }
    }
}

function thingsEqual(thing1, thing2) {
    return thing1.place === thing2.place
        && thing1.name === thing2.name;
}

removeDuplicates(things.thing, thingsEqual);
Tim Down
Two objects won't evaluate equal, even if they share the same properties and values.
kennebec
Yes, I know. But fair point, I've failed to read the question correctly: I hadn't spotted that it was objects with identical properties he needed to weed out. I'll edit my answer.
Tim Down
Thanks Tim. I'll check that out tonight.
Travis
A: 

If you can wait to eliminate the duplicates until after all the additions, the typical approach is to first sort the array and then eliminate duplicates. The sorting avoids the N * N approach of scanning the array for each element as you walk through them.

The "eliminate duplicates" function is usually called unique or uniq. Some existing implementations may combine the two steps, e.g., prototype's uniq

This post has few ideas to try (and some to avoid :-) ) if your library doesn't already have one! Personally I find this one the most straight forward:

    function unique(a){
        a.sort();
        for(var i = 1; i < a.length; ){
            if(a[i-1] == a[i]){
                a.splice(i, 1);
            } else {
                i++;
            }
        }
        return a;
    }  

    // Provide your own comparison
    function unique(a, compareFunc){
        a.sort( compareFunc );
        for(var i = 1; i < a.length; ){
            if( compareFunc(a[i-1], a[i]) === 0){
                a.splice(i, 1);
            } else {
                i++;
            }
        }
        return a;
    }
maccullt
That won't work for generic objects without a natural sort order.
Tim Down
True, I added a user-supplied comparison version.
maccullt
Your user-supplied comparison version won't work because if your comparison function is `function(_a,_b){return _a.a===_b.a }` then the array won't be sorted.
graham.reeds
That is an invalid compare function. From https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/sort...function compare(a, b){ if (a is less than b by some ordering criterion) return -1; if (a is greater than b by the ordering criterion) return 1; // a must be equal to b return 0;}...
maccullt