How do I make a checksum from a JavaScript Object?

striking picture striking · Jul 23, 2014 · Viewed 24.9k times · Source

I need to make a checksum from a JavaScript Object.
Unfortunately, there does not seem to be an easy way to accomplish this because of JavaScript's Object ordering. For example, take these Objects:

var obj1 = {type:"cake",quantity:0}
  , obj2 = {quantity:0,type:"cake"};

I consider these Objects equal in data, and would like their checksums to be the same. I really don't care about the order of the Object just as long as the data in them is the same.
Alas, JSON.stringify of both of them is actually not equal; as the only way to make a checksum of an Object is via its String representation, and the JSON.stringify-ed representations are not equal, my checksums will not be equal!
One solution I have come up with is to recreate the Object based on a predefined schema, like so:

var schema = ["type","quantity"];
function sortify(obj,schema){
  var n={};
  for(i in schema)
    n[schema[i]]=obj[schema[i]];
  return n
}

Running JSON.stringify(sortify(obj1,schema))==JSON.stringify(sortify(obj2,schema)) will return true... but at the price of creating a new Object and shuffling around data.

My other solution is to replace the JSON.stringify method with one that picks keys from a predefined schema and stringifying their values, then joining them together. The function reads:

function smarterStringify(obj,schema){
  var s="";
  for(i in schema)
    s+=JSON.stringify(obj[schema[i]]);
  return s
}

Ignoring the fact that this method doesn't return correct JSON (it's close enough as an example of what I'm trying to do), it is a massive improvement over the first one in speed (at least in my Chrome OS browser, you can check it yourself here: http://jsperf.com/sort-then-json-stringify-vs-smarter-stringify), and of course it makes the two Object String representations equal!

However, I was just wondering if I had missed something and there was a built-in method for something like this all along that didn't a) drive the JavaScript GC into a pathological case or b) do way too many String concatenations. I'd rather not do those.

Answer

jfriend00 picture jfriend00 · Jul 23, 2014

You can collect the keys into an array with Object.keys(), sort that array and then checksum the keys/values in that known, predictable order. I don't know of any way to use JSON.stringify() with all the sorted keys at once though so you'd have to do your own checksum.

I am not aware of any built-in method for something like this. Object keys are NOT guaranteed to be in any particular order so it would not be safe to rely on that.


If you don't have nested objects or arrays as property values, then you could do something like this:

// creates an array of alternating property name, property value
// with properties in sorted order
// then stringify's that array
function stringifyPropsInOrder(obj) {
    var keys = Object.keys(obj).sort();
    var output = [], prop;
    for (var i = 0; i < keys.length; i++) {
        prop = keys[i];
        output.push(prop);
        output.push(obj[prop]);
    }
    return JSON.stringify(output);
}

function compareObjects(a, b) {
    return stringifyPropsInOrder(a) === stringifyPropsInOrder(b);
}

If you want faster performance, you don't have to stringify (that was just done here to save code). You could just return the flattened output array and compare the arrays directly.


If you could have embedded objects as property values, then some more work has to do to recusively expand those into the same flattened array of properties/values.