2

I'm trying to flatten any type of json object with unlimited embedded levels from something like this:

[
    {
        "one": 1,
        "two": 2,
        "three": [
            {
                "four": 4,
                "five": 5
            },
            {
                "six": 6,
                "seven": 7
            }
        ]
    },
    {
        "one": 1,
        "two": 2
    }
]

into something like this (desired result):

{
    "0": {
        "prefix[0]one": 1,
        "prefix[0]three[0]five": 5,
        "prefix[0]three[0]four": 4,
        "prefix[0]three[1]seven": 7,
        "prefix[0]three[1]six": 6,
        "prefix[0]two": 2
    },
    "1": {
        "prefix[1]one": 1,
        "prefix[1]two": 2
    }
}

Now I have found a script that works very well as far as flattening the data (good starting point), but I am trying to figure out how to tweak the code so that it groups each top level array/object into it's own array/object rather than one large single object like the script does on that page (see accepted answer "JSON.flatten").

Here is the cleanest version I have so far to achieve the desired result which is not much different from the original. I have tried much more than this but its too sloppy to post.

JSON.flatten = function(data) {
    var result = {};
    function recurse (cur, prop) {
        if (Object(cur) !== cur) {
            result[prop] = cur;
        } else if (Array.isArray(cur)) {
             for(var i=0, l=cur.length; i<l; i++)
                 recurse(cur[i], prop + "[" + i + "]");
            if (l == 0)
                result[prop] = [];
        } else {
            var isEmpty = true;
            for (var p in cur) {
                isEmpty = false;
                recurse(cur[p], prop ? prop+p : p);
            }
            if (isEmpty && prop)
                result[prop] = {};
        }
    }
    recurse(data, "prefix");
    return result;
}

Here's what the current code produces:

{
    "prefix[0]one": 1,
    "prefix[0]three[0]five": 5,
    "prefix[0]three[0]four": 4,
    "prefix[0]three[1]seven": 7,
    "prefix[0]three[1]six": 6,
    "prefix[0]two": 2,
    "prefix[1]one": 1,
    "prefix[1]two": 2
}

Note: I do not necessarily need to use the sample code provided. Any code that achieves the desired result will do.

Thanks All!

UPDATE:

I've realized my desired output was needing to be something more like so:

{
    "0": {
        "prefix[0][one]": 1,
        "prefix[0][three][0][five]": 5,
        "prefix[0][three][0][four]": 4,
        "prefix[0][three][1][seven]": 7,
        "prefix[0][three][1][six]": 6,
        "prefix[0][two]": 2
    },
    "1": {
        "prefix[1][one]": 1,
        "prefix[1][two]": 2
    }
}

But even after getting it to format like that using CViejo's suggestions I ended up finding out that this would not work entirely because of the nested objects. They would have had to been objects as well down to an unlimited level. So I'm taking Tomalak's suggestion and trying out the recurse approach and assign values as I go. I just wanted to thank both you guys for providing some great suggestions!

Community
  • 1
  • 1
Rick
  • 712
  • 5
  • 23
  • The obvious question is: What's the purpose of this? Destroying structured data doesn't seem very useful, especially since all you do is exchanging nested structure with a key/value list with structured key - that needs to be parsed again at some point, otherwise you wouldn't bother maintaining the structure in the key. So, why not work with the data as it is? – Tomalak Aug 27 '15 at 07:16
  • I'm using it to inject data into form elements.. – Rick Aug 27 '15 at 07:26
  • Can you elaborate on this a bit more? – Tomalak Aug 27 '15 at 07:28
  • I have a bunch of code already in place working great and this is the last piece to the puzzle. If I can get it to work I will not have to alter any other existing code which is what I'm hoping for. But it's like this.. I'm flattening input names. Each object contains a group of input names which I will search that particular fieldset for and inject the values. Something like so would be within the loop for each fieldset: $fieldset.find(':input[name="' + name + '"]'); so that the script can locate the input it's needing to inject the value into.. Hope that makes sense.. – Rick Aug 27 '15 at 07:40
  • No, not really. Wouldn't it be easier to simply recurse the nested object and assign values to the form elements as you go, instead of first applying some byzantine transformation to the object? – Tomalak Aug 27 '15 at 08:01
  • I would definitely be interested in doing anything that works. Would you happen to be able to show any examples? – Rick Aug 27 '15 at 08:06
  • Sure, but I would need a little more context for that, i.e. your HTML layout, the JSON data you get from the server, the desired effect and your current code. All reduced to a [meaningful minimum](http://stackoverflow.com/help/mcve), of course. – Tomalak Aug 27 '15 at 10:31

1 Answers1

1

I don't see the point in adjusting that flatten function which already does what it says right. Instead, since all you want is to format your data (for whatever reason), why don't you format the flattened output to fit your needs?

function formatFlat(obj, prefix){

    var result = {};

    for (var key in obj) {  

        var newKey = key.replace(prefix + "[", "").split("]")[0];

        if(!result[newKey]){
            result[newKey] = {};
        }

        result[newKey][key] = obj[key];
    }

    return result;
}

And the call it like:

console.log( formatFlat(JSON.flatten(yourData), "prefix") );
cviejo
  • 4,388
  • 19
  • 30
  • I like where you're going with this by leaving the flatten function alone because it does work nicely. This might lead me to my answer.. – Rick Aug 27 '15 at 08:10
  • 1
    That function outputs your desired format. That being said, i agree with the comments above, unless you have major reasons for this like some external code you cannot modify, you should probably consume the original object. – cviejo Aug 27 '15 at 08:58