Skip to content Skip to sidebar Skip to footer

Compressing Object Hierarchies In Javascript

Is there a generic approach to 'compressing' nested objects to a single level: var myObj = { a: 'hello', b: { c: 'world' } } compress(myObj) == { a: 'hello

Solution 1:

function flatten(obj, includePrototype, into, prefix) {
    into = into || {};
    prefix = prefix || "";

    for (var k in obj) {
        if (includePrototype || obj.hasOwnProperty(k)) {
            var prop = obj[k];
            if (prop && typeof prop === "object" &&
                !(prop instanceof Date || prop instanceof RegExp)) {
                flatten(prop, includePrototype, into, prefix + k + "_");
            }
            else {
                into[prefix + k] = prop;
            }
        }
    }

    returninto;
}

You can include members inherited members by passing true into the second parameter.

A few caveats:

  • recursive objects will not work. For example:

    var o = { a: "foo" };
    o.b = o;
    flatten(o);
    

    will recurse until it throws an exception.

  • Like ruquay's answer, this pulls out array elements just like normal object properties. If you want to keep arrays intact, add "|| prop instanceof Array" to the exceptions.

  • If you call this on objects from a different window or frame, dates and regular expressions will not be included, since instanceof will not work properly. You can fix that by replacing it with the default toString method like this:

    Object.prototype.toString.call(prop) === "[object Date]"Object.prototype.toString.call(prop) === "[object RegExp]"Object.prototype.toString.call(prop) === "[object Array]"

Solution 2:

Here's a quick one, but watch out, b/c it will not work w/ arrays and null values (b/c their typeof returns "object").

var flatten = function(obj, prefix) {
  if(typeof prefix==="undefined") {
    prefix="";
  }
  var copy = {};
  for (var p in obj) {
    if(obj.hasOwnProperty(p)) {
      if(typeof obj[p] ==="object") {
        var tmp = flatten(obj[p], p +"_");
        for(var q in tmp) {
          if(tmp.hasOwnProperty(q)) {
            copy[prefix+ q] = tmp[q];
          }
        }
      }
      else {
        copy[prefix+ p] = obj[p];
      }
    }
  }
  return copy;
}

var myObj = {
  a: "level 1",
  b: {
    a: "level 2",
    b: {
      a: "level 3",
      b: "level 3"
    }
  }
}

var flattened = flatten(myObj);

Solution 3:

Here's a quick CoffeeScript version based off Matthew Crumley's answer (I didn't use includePrototype as I had no need for it):

flatten = (obj, into = {}, prefix = '', sep = '_') ->
  for own key, prop of obj
    iftypeof prop is'object'and prop not instanceof Date and prop not instanceof RegExp
      flatten prop, into, prefix + key + sep, sep
    elseinto[prefix + key] = prop
  into

And a basic unflatten version, which would undoubtedly fail with repeated separators and other such trickiness:

unflatten = (obj, into = {}, sep = '_') ->
  for own key, prop of obj
    subKeys = key.split sep
    sub = intosub = (sub[subKey] or= {}) for subKey in subKeys[...-1]
    sub[subKeys.pop()] = propinto

FWIW, I use these functions to push object graphs into Redis hashes, which only support a single depth of key/value pairs.

Post a Comment for "Compressing Object Hierarchies In Javascript"