0

I have a requirement where I have an object like obj={ 'a.b.c' : d } and I would like it to get converted to {a:{b:{c:d}}}

Is there any way I can achieve this in JavaScript?

  • There is no such object as `{a.b.c:d}`. That is invalid syntax. –  Aug 04 '16 at 13:54
  • @torazaburo It's perfectly valid if you create an empty object and set properties using the `obj[key]` syntax, although I don't see why you would. It also works if you set the key as a string during object declaration. – kag359six Aug 04 '16 at 14:01
  • @kag359six Then the OP should fix his question to set it as a string, as it stands now, it is invalid syntax. –  Aug 04 '16 at 14:03
  • @torazaburo You are certainly correct, I revised his question, which is currently pending review. – kag359six Aug 04 '16 at 14:05
  • Possible duplicate of [Looking for an FP algorithm to compose objects from dot-separated strings](http://stackoverflow.com/questions/22864748/looking-for-an-fp-algorithm-to-compose-objects-from-dot-separated-strings) – Mike Cluck Aug 04 '16 at 16:42

2 Answers2

0

Here's a solution (EDITED: code is more complex than before but it gives the result you want, let me know if something doesn't work):

var obj = {
    'a.b.c': 22,
    'a.b.d.e': 42
}

var newObj = {};

for (var key in obj) {

    if (obj.hasOwnProperty(key)) {

        var keyList = key.split('.');
        newObj = generateNewObject(keyList, keyList.length - 1, newObj, obj[key]);

    }

}

console.log(newObj);

function generateNewObject(keys, index, existingObj, value) {

    if (index < 0) {
        return value;
    }

    var lastKey = keys[index--];
    var existingProperty = getProperty(existingObj, lastKey);

    if (existingProperty != null && !objectsAreEqual(existingProperty, value)) {

        var valueKey = keys[index + 2];
        existingProperty[lastKey][valueKey] = value[valueKey];
        value = existingProperty;


    } else {

        var subObj = {};
        subObj[lastKey] = value;
        value = subObj;
    }

    return generateNewObject(keys, index, existingObj, value);

}

function objectsAreEqual(obj1, obj2) {

    for (var key in obj1) {

        if (obj1.hasOwnProperty(key)) {

            var prop = getProperty(obj2, key);
            if (prop == null) {
                return false;
            }

        }

    }

    return true;

}

function getProperty(obj, keyDesired) {

    for (var key in obj) {

        if (obj.hasOwnProperty(key)) {

            if (key === keyDesired) {

                return obj;

            } else {

                var prop = getProperty(obj[key], keyDesired);

                if (prop != null) {
                    return prop;
                }

            }

        }

    }

    return null;

}

I don't know why you would have an object named that way, but this code will do the trick for each key in an object. This will not work correctly on nested objects such as {'a' : { 'b' { 'c' : {{'d' : 'e'}}}}}. You would have to repeat the for-loop part each time the value is a JavaScript object.

EDIT

I modified the code so it recognizes when two properties are the same such as the example { 'a.b.c' : 22 }, 'a.b.c.d.e' : 42. Sorry if it is hard to go through, but basically the generateNewObject method is the real meat of it. The two functions below it are just helper methods.

kag359six
  • 1,693
  • 2
  • 16
  • 21
0

Array.reduce mostly is a good choice when it comes to handling/transforming of more complex data structures. An approach that solves the given problem generically whilst taking edge cases into account then might look similar to the next provided example ...

var
    d   = 'd',
    q   = 'q',

    obj = {
        'i.k.l.m.n.o.p' : q,
        'a.b.c'         : d,
        'foo'           : 'bar',
        ''              : 'empty'
    };

function parseIntoNestedTypes(type) {
    return Object.keys(type).reduce(function (collector, integralKey) {

        var
            nestedType          = collector.target,
            fragmentedKeyList   = integralKey.split('.'),
            nestedTypeRootKey   = fragmentedKeyList.shift(),
            nestedTypeEndValue  = collector.source[integralKey];

        if (fragmentedKeyList.length === 0) {

            nestedType[nestedTypeRootKey] = nestedTypeEndValue;
        } else {
            nestedType[nestedTypeRootKey] = fragmentedKeyList.reduce(function (collector, key, idx, list) {

                var
                    partialType = collector.partialType || collector.type;

                if (idx < (list.length - 1)) {

                    partialType[key] = {};
                } else {
                    partialType[key] = collector.value;
                }
                collector.partialType = partialType[key];

                return collector;

            }, {

                value : nestedTypeEndValue,
                type  : {}

            }).type;
        }
        return collector;

    }, {

        source: type,
        target: {}

    }).target;
}

console.log('parseIntoNestedTypes :: type', JSON.stringify(obj));
console.log('parseIntoNestedTypes :: nestedType', JSON.stringify(parseIntoNestedTypes(obj)));

console.log('parseIntoNestedTypes :: type, nestedType : ', obj, parseIntoNestedTypes(obj));
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37