Matching Arrays and Objects

I came across a fun little problem when I was playing around with JavaScript a couple of days ago: how do you compare 2 arrays? Comparing variables is usually easy, but comparing arrays (or objects for that matter) is a much trickier business. Let me show you can I mean:

var myString = 'hello world',
    myArray = ['hello', 'world'];
alert(myString === 'hello world'); // true
alert(myArray === ['hello', 'world']); // false

I can’t find any good reason for this behaviour. At a guess, each array is a new creation of the Array constructor and there are a few things hidden deep in the prototype that means one array will never match another. I think this is because Arrays weren’t part of the original JavaScript spec, they were just Objects until enough people complained (hence alert(typeof []); returns “object”)

Annoyed by this, I hit Google. I managed to find a few scripts that explained how to check arrays. Essentially, you have to loop through the first array and create a count of the number of times that element appears (taking the time to analyse the element so that 1 doesn’t match “1” etc). Then you have to loop through the second array and deduct your count. If the counts at the end are 0, your arrays match. It’s not that this is difficult, but surely there should be an easier way to do this. Anyway, here’s the script that I put together to test that two arrays match:

// Based on a script found at 
// http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256BFB0077DFFD
function arraysMatch(array1, array2) {

    var temp = {},
        i,
        il,
        key,
        toString = Object.prototype.toString,
        
// Start by checking that both array1 and array2 are arrays and that their lengths
// match.
        match = toString.call(array1) === '[object Array]' &&
                toString.call(array2) === '[object Array]' &&
                array1.length >>> 0 === array2.length >>> 0;

    if (match) {
    
// Go through each of the elements in array1 and create a checking object
// containing a count of each of the elements. This allows the element to exist
// in array1 multiple times.
        for (i = 0, il = array1.length; i < il; i += 1) {
            key = toString.call(array1[i]) + '~' + array1[i];
            if (temp[key] === undef) {
                temp[key] = 0;
            }
            temp[key] += 1;
        }

// Do the same again with array2 but this time deduct the count in the checking
// object. If that count doesn't exist or it drops below 0 then we know the
// arrays don't match.
        for (i = 0, il = array2.length; i < il; i += 1) {
            key = toString.call(array2[i]) + '~' + array2[i];
            if (temp[key] === undef || temp[key] <= 0) {
                match = false;
                break;
            } else {
                temp[key] -= 1;
            }
        }
        
// If we've been successful so far, check the checking object. Every one of the
// keys should have been reduced to 0 if the arrays match.
        if (match) {
            for (key in temp) {
                if (temp.hasOwnProperty(key)) {
                    if (temp[key] !== 0) {
                        match = false;
                        break;
                    }
                }
            }
        }
    
    }
    
    return match;

}

Matching two objects is even more fun. I had to use a loop label - I've never used one of those before! To match two objects, you loop through the keys of the first object and check they're all in the second, then loop through the second and check they're all in the first. If both objects have the same keys you loop through again and check that the keys match. 3 damn loops, assuming that none of the properties are objects...

// Based on a script found at 
// http://stackoverflow.com/questions/1068834/object-comparison-in-javascript
function objectsMatch(object1, object2) {

    var p,
        match = true;
    
// Start by going through each of the properties in object1. Check that they
// each exist in object2.
    for (p in object1) {
        if (object1.hasOwnProperty(p)) {
            if (object2[p] === undef) {
                match = false;
                break;
            }
        }
    }

// If everything existed in object2, do the same check using the object2
// properties in object1.
    if (match) {
        for (p in object2) {
            if (object2.hasOwnProperty(p)) {
                if (object1[p] === undef) {
                    match = false;
                    break;
                }
            }
        }
    }
    
// Now that we know both objects have the same properties, check to see if
// those properties match. We do this check last since the this function can
// recurse here so we should save ourselves some of the processing work.
    if (match) {
        checkLoop:
        for (p in object1) {
            if (object1.hasOwnProperty(p)) {
                switch (typeof object1[p]) {
                case 'object':
                    if (!objectsMatch(object1[p], object2[p])) {
                        match = false;
                        break checkLoop;
                    }
                    break;
                case 'function':
                    if (object1[p].toString() !== object2[p].toString()) {
                        match = false;
                        break checkLoop;
                    }
                    break;
                default:
                    if (object1[p] !== object2[p]) {
                        match = false;
                        break checkLoop;
                    }
                }
            }
        }
    }
            
    return match;
    
}

This seems like a lot to test arrays or objects for equivalence, but at least the functions are relatively simple. You're welcome to use these functions but please leave the original credit in place.

Leave a Reply

Your email address will not be published. Required fields are marked *