JSFiddle: http://jsfiddle.net/3WdzL/1/

I need to convert locale JS object files to flattened versions and back again:

Orig locale object:

var localeObj = {
    toolbar: {
        link: {
            back: 'Back',
            menu: 'Menu',
        },
        flatTest: 'something'
    },
    countries: [
        ["AF", "Afghanistan"],
        ["AX", "Åland Islands"],
        ['nested', [1, 2, 3, 4]],
        ["AL", "Albania"]
    ]
};

Using the following function:

function flattenObj(obj) {
    var flattenedObj = {};

    var walk = function(obj, stringMap) {

        for(k in obj) {
            var computedKey = stringMap + (stringMap ? '.' + k : k);

            if(typeof obj[k] !== 'object') {
                flattenedObj[computedKey] = obj[k];
            } else {
                walk(obj[k], computedKey);
            }
        }
    };

    walk(obj, '');
    return flattenedObj;
}

Would produce a flattened object:

{
    toolbar.link.back: Back
    toolbar.link.menu: Menu
    toolbar.flatTest: something
    countries.0.0: AF
    countries.0.1: Afghanistan
    countries.1.0: AX
    countries.1.1: Åland Islands
    countries.2.0: nested
    countries.2.1.0: 1
    countries.2.1.1: 2
    countries.2.1.2: 3
    countries.2.1.3: 4
    countries.3.0: AL
    countries.3.1: Albania 
}

Converting back with the following func works fine for objects:

function deepenObj(obj) {
  var deepenedObj = {}, tmp, parts, part;

  for (var k in obj) {
    tmp = deepenedObj;
    parts = k.split('.');

    var computedKey = parts.pop();

    while (parts.length) {
      part = parts.shift();
      tmp = tmp[part] = tmp[part] || {};
    }

    tmp[computedKey] = obj[k];
  }

  return deepenedObj;
}

But produces a structure like this for the arrays:

region: {
    country: {
        0: {
            0: 'AF',
            1: 'Afghanistan'
        },
        ...
        2: {
            0: 'nested',
            1: {
                0: 1,
                1: 2,
                3: 4,
                4: 5
            }
        }
    }
}

Obviously this isn't the desired results for the arrays and I haven't been able to come up with a safe, elegant or even working solution yet. PS I am happy to save the arrays to strings differently if it makes converting back easier. Thanks!

有帮助吗?

解决方案

You should either keep track if an object is actually an array:

var walk = function(obj, stringMap) {
    if (Array.isArray(obj) {
        for (var k = 0; k < obj.length; k++)
            var computedKey = stringMap ? stringMap + ',' + k : k;
    } else {
        for (var k in obj) {
            var computedKey = stringMap ? stringMap + '.' + k : k;
        ...

Then, when deepening:

for (var k in obj) {
    tmp = deepenedObj;
    parts = ["."].concat(k.split(/([\.,])/));

    var computedKey = parts.pop(), sign;

    while (parts.length) {
        sign = parts.shift();
        part = !parts.length ? computedKey : parts.shift();
        tmp = tmp[part] = tmp[part] || (sign === "," ? [] : {});
    }

    tmp[computedKey] = obj[k];
}

Note that Array.isArray could be undefined. You can use obj instanceof Array instead.

This solution works if localeObj is an object literal and not an array, because the first point/comma isn't saved in the computed key. You can modify the function if you need to.

The trick here is to use an unusual behaviour of split that pushes captured groups in the splitted array when used with regular expressions, so before every key part there's the proper separator.

其他提示

Use JSON.stringify() and JSON.parse():

var flattenedObj = JSON.stringify(localeObj);

vat deepenedObj = JSON.parse(flattenedObj);

Demo

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top