4
\$\begingroup\$

While responding to a SO question, I wrote the following method to convert a flat key structure to a hierarchical one as given below

In

{
   property1: 'value1',
   property2.property3: 'value2',
   property2.property7: 'value4',
   property4.property5.property6: 'value3',

}

Out

{
    property1: 'value1',
    property2: {
        property3: 'value2',
        property7: 'value4'
    },
    property4: {
        property5: {
            property6: 'value3'
        }
    }
}

Converter:

function convert(obj) {
    var res = {}, i, j, splits, ref, key;
    for (i in obj) {
        if (obj.hasOwnProperty(i)) {
            splits = i.split('.');
            ref = res;
            for (j = 0; j < splits.length; j++) {
                key = splits[j];
                if (j == splits.length - 1) {
                    ref[key] = obj[i];
                } else {
                    ref = ref[key] = ref[key] || {};
                }
            }
        };
    }
    return res;
}

Can this be improved?

Demo: Fiddle

\$\endgroup\$
3
\$\begingroup\$

The main complexity of this function arises from using a plain for-in loop to loop over object keys - this adds two extra indentation levels instead of just one level if one were to use a higher-level looping construct. So I would use some library function or create something like:

function eachKeyValue(obj, fun) {
    for (var i in obj) {
        if (obj.hasOwnProperty(i)) {
            fun(i, obj[i]);
        }
    }
}

Within this loop, there's another low-level for-loop, which I would again replace, say... with an Array.forEach method.

Now, in your code the loop goes over all the parts of the key, checking each time whether it's the last part. Instead of this, I would first remove the last part, loop over all the other parts to ensure all these are initialized, and then simply assign the value to the last position.

With some additional improvements to variable names, I would write it as such:

function convert(obj) {
    var result = {};
    eachKeyValue(obj, function(namespace, value) {
        var parts = namespace.split(".");
        var last = parts.pop();
        var node = result;
        parts.forEach(function(key) {
            node = node[key] = node[key] || {};
        });
        node[last] = value;
    });
    return result;
}

This code has just 2 levels of nesting compared to the original 4.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.