1
var array1 = [{ "name" : "foo" , "age" : "22"}, { "name" : "bar" , "age" : "33"}];
var array2 = [{ "name" : "foo" , "age" : "22"}, { "name" : "buz" , "age" : "35"}];

What's the fastest way to have (no duplicates, name is the identifier):

[{ "name" : "foo" , "age" : "22"}, { "name" : "bar" , "age" : "33"}, { "name" : "buz" , "age" : "35"}];

With and without jquery if possible.

plus-
  • 45,453
  • 15
  • 60
  • 73

4 Answers4

5

Here's a general purpose function that would merge an arbitrary number of arrays, preventing duplicates of the passed in key.

As it is merging, it creates a temporary index of the names used so far and only merges new elements that have a unique name. This temporary index should be much faster than a linear search through the results, particularly as the arrays get large. As a feature this scheme, it filters all duplicates, even duplicates that might be in one of the source arrays.

If an element does not have the keyName, it is skipped (though that logic could be reversed if you want depending upon what error handling you want for that):

var array1 = [{ "name" : "foo" , "age" : "22"}, { "name" : "bar" , "age" : "33"}];
var array2 = [{ "name" : "foo" , "age" : "22"}, { "name" : "buz" , "age" : "35"}];

function mergeArrays(keyName /* pass arrays as additional arguments */) {
    var index = {}, i, len, merge = [], arr, name;

    for (var j = 1; j < arguments.length; j++) {
        arr = arguments[j];
        for (i = 0, len = arr.length; i < len; i++) {
            name = arr[i][keyName];
            if ((typeof name != "undefined") && !(name in index)) {
                index[name] = true;
                merge.push(arr[i]);
            }
        }
    }
    return(merge);
}

var merged = mergeArrays("name", array1, array2);

// Returns:
// [{"name":"foo","age":"22"},{"name":"bar","age":"33"},{"name":"buz","age":"35"}]

You can see it work here: http://jsfiddle.net/jfriend00/8WfFW/

When this algorithm is run against the Matt algorithm in jsperf using larger arrays, this algorithm is around 20x faster:

enter image description here

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Added [jsPerf test results](http://jsperf.com/merge-arrays) comparing this algorithm vs. the one Matt proposed (lineary search vs. hash lookup using javascript object). – jfriend00 Mar 12 '12 at 23:06
1

What you have are completely different objects, and there is nothing built into JavaScript to detect identical objects; i.e. objects which have the same attributes, so we have to write our own function:

function merge(set1, set2) {
    // Already put the elements of set1 in the result array
    // see Array.slice
    var result = set1.slice(0);

    // Utility function which iterates over the elements in result
    // and returns true if an element with the same name already
    // exists. false otherwise
    function exists(obj) {
        for (var i=0;i<result.length;i++) {
            if (result[i].name == obj.name) {
                return true;
            }
        }

        return false;
    }

    // Now simply iterate over the second set and add elements
    // which aren't there already using our exists() function.
    for (var i=0;i<set2.length;i++) {
        if (!exists(set2[i])) {
            result.push(set2[i]);
        }
    }

    return result;
}

You'd then call it with;

var result = merge(array1, array2);

To become more confident with object equality try the following experiment;

var a = { "test": 1 };
var b = { "test": 1 };
var aClone = a;

alert(a == a); // true
alert(a == b); // false
alert(a == aClone); // true
Matt
  • 74,352
  • 26
  • 153
  • 180
  • 2
    The OP asked for the "fastest" way to do this. I rather doubt that doing a linear search in the result array for an existing name is the fastest way to detect dups. Using an object (which is essentially a hash table with unique keys) is a much faster way to keep track of which items are already in the result. See [this answer](http://stackoverflow.com/a/9672050/816620) for that type of implementation. – jfriend00 Mar 12 '12 at 17:52
0

I don't think that plain javascript offers anything better than iterating the array's and manually implementing the logic for that. What I would advice is to use the awesome underscore.js library that provides many functional-like facilities to handle arrays and collections in general; to solve your problem for example this could work:

http://documentcloud.github.com/underscore/#union

jQuery is another option, but it is more a DOM-manipulation browser-oriented library, while underscore is made to deal with these kind of problems.

Aldo Stracquadanio
  • 6,167
  • 1
  • 23
  • 34
-2

The first way I can think of:

array3 = [];
for(object in array1) {
    var match=false;
    for(already in array3) {
        if (already==object) {
            match=true;
            break; } }
    if (match) array3.push(object); }
DanRedux
  • 9,119
  • 6
  • 23
  • 41
  • 1
    This won't work as *all* the objects are different; they are merely *identical* because they have similar members/ attributes. `already==object` will never evaluate to `true` – Matt Mar 12 '12 at 17:31
  • Don't `for-in` an Array. Iterate using `for` and the `.length` of the Array instead. –  Mar 12 '12 at 17:31
  • 1
    @Matt: Actually, he's comparing property names instead of values... but either way it would be entirely wrong. –  Mar 12 '12 at 17:35