johnnysc
johnnysc

Reputation: 13

Traverse json object and move child obj properties to parent based on key name

I have a JSON object that I use as a theme (It's for Appcelerator, but really, it doesn't matter since this is more of a JSON question.) I do have have access to underscore.js if that helps with traversals/functions...but pure JS would work as well.

I need to traverse the JSON tree and move a child objects properties to it's parents if that key is a certain value, but also omit that object if it it's key another value. This needs to happen at multiple levels of the JSON tree. So I need some sort of recursive function.

For an example, let's say I need to move all properties of a child object with a key of ANDROID to it's parent object, but ignore the object and properties if a key is IOS:

Original Object

var theme = {
    Form: {
        LabelTop: {
            backgroundColor: 'red',
            fieldBorderColor: '#cacacf',
            rowHeight: 40,
            hintText: {
                font: {
                    fontSize: 12
                },
                colorOn: '#3accf1',
                colorOff: '#cacacf',
                ANDROID: {
                    width: 200
                }
            },

        }
    },
    Modal: {
        id: 'md',
        backgroundColor: '#fff',
        IOS: {
            color: 'red'
        }
    }
};

I'd like to be able to call something like this:

var newTheme = parsePlatform(theme, 'ANDROID', 'IOS');

Where theme is the object to search, ANDROID is the key to merge and IOS is the key to skip

With this example I'm moving/replacing all properties of a parent object with the child properties if the key is ANDROID, but I'm not including any with a parent key of IOS. In this example I would expect the output to be this:

Modified Object

var theme = {
    Form: {
        LabelTop: {
            backgroundColor: '#fff', // replaced
            fieldBorderColor: '#cacacf',
            rowHeight: 40,
            hintText: {
                font: {
                    fontSize: 12
                },
                colorOn: '#3accf1',
                colorOff: '#cacacf',
                width: 200 // 'ANDROID' prop moved
            }
        }
    },
    Modal: {
        // IOS removed
        id: 'md',
        backgroundColor: '#fff'
    }
};

Notice Modal doesn't have a color property because i'm only looking for ANDROID, not IOS

I've been able to get the results i'm looking for if I know the JSON depth and hard code it in. Once I move to a recursive function everything blow up. Any help would be appreciated.

Upvotes: 1

Views: 2523

Answers (1)

epoch
epoch

Reputation: 16615

Normally I would ask what you have tried, but this question was interesting to me, the following is not heavily tested, but seems to do the job:

function mergeExtend(obj, merge, skip) {
    var o, o1, nObj = {};
    for (o in obj) {
        // ensure we are only checking this obj's keys
        if (obj.hasOwnProperty(o)) {
            // check if we have keys to merge
            if ((merge || []).indexOf(o) > -1) {
                // if something was defined in merge, we expect the value to be an object
                for (var o1 in obj[o]) {
                    if (obj[o].hasOwnProperty(o1)) {
                        // put child obj val under parent key
                        nObj[o1] = obj[o][o1];
                    }
                }
            // ensure we are not skipping this key
            } else if ((skip || []).indexOf(o) === -1) {
                if (typeof obj[o] === 'object') {
                    // go recursive if we are not merging and val is an object
                    nObj[o] = mergeExtend(obj[o], merge, skip);
                } else {
                    // otherwise just copy the key val over
                    nObj[o] = obj[o];
                }
            }
        }
    } 

    return nObj;
}

console.log(mergeExtend(theme, ['ANDROID'], ['IOS']));

And the FIDDLE

Upvotes: 1

Related Questions