Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Okay.

I have a JSON object where each item of the object is a top level object...there are no nested relationships. Some of the items are nested, but everything is top level.

The way a nested relationship is setup is by the ID value.

  • Item a = item_a
  • Item b related to a = item_a.item_b
  • Item c = item_c
  • Item d related to c = item_c.item_d
  • Item e related to d = item_c.item_d.item_e

I've tried splitting on the . and looping over the total json object and sorting them. I've tried 4 or 5 different ways that I can think of and nothing is working properly.

What I am trying to end up with is a single object that looks like this.

Obj = [{
    item_a: {
        nestedItems: [{
            item_b: {}
        }]
    },
    item_c: {
        nestedItems: [{
            item_d: {
                nestedItems: {
                    item_e: {}
                }
            }
        }]
    }    
}];

I can not change the output so let's not ask that question. This is the problem I'm trying to solve. Can anyone help me understand the best way to sort this?

Here is my current state...but it's far from complete:

function getNestedAttributes(attrs) // attrs is the JSON object
{
    var nested = [];

    for ( var i=0; i<attrs.length; i++ )
    {
        var idLevels = splitId(attrs[i].id); // split on . return array

        if ( idLevels.length == 1 )
        {
            nested[idLevels[0]] = attrs[i];
            nested[idLevels[0]]['nestedAttributes'] = [];
        }

        if ( idLevels.length > 1 )
        {
            nested[idLevels[0]]['nestedAttributes'].push(attrs[i]);
        }

    }

    console.log(nested);

}

The above is working to a point. I'm making everything an array, which is fine, but it only works for objects that have one level nested. Some items have up to 4 levels nested.

There has to be a better way to loop through this in JS.

JSON:

attributes: [
{
    id: "item_a",
    name: "Item A Name",
    ...
},
{
    id: "item_a.item_b",
    name: "Item B Name",
    ...
},
{
    id: "item_a.item_c",
    name: "Item C Name",
    ...
},
{
    id: "item_d",
    name: "Item D Name",
    ...
},
{
    id: "item_d.item_e",
    name: "Item E Name",
    ...
},
{
    id: "item_d.item_e.item_f",
    name: "Item F Name",
    ...
}];

The above is a very basic example, but this is what the output looks like.

Ideally I'd end up with the following:

attributes: [
{
    id: "item_a",
    name: "Item A Name",
    nestedAttributes: [{
        id: "item_a.item_b"
        name: "Item B Name"
        ...
    },
    {
        id: "item_a.item_c"
        name: "Item C Name"
        ...
    }],
    ...
},
{
    id: "item_d",
    name: "Item D Name",
    nestedAttributes: [{
        id: "item_d.item_e"
        name: "Item E Name"
        nestedAttributes: [{
            id: "item_d.item_e.item_f",
            name: "Item F Name",
            ...
        }],
        ...
    }],
    ...
}];
share|improve this question
1  
You need to post what the original JSON looks like. – Pointy Nov 27 '12 at 14:49
Done. I added a basic example to give you a feel for what I'm working with. – Seth Nov 27 '12 at 15:03
Oh wow, that's -not- how to do JSON. I know it's not your code, I just want to tell you I feel for you. – Mike Robinson Nov 27 '12 at 15:11
For clarity, demonstrate where the 'name' attribute should end up in the final object. – hexparrot Nov 27 '12 at 15:22
Did you try to split with regex? Say something like var re=/item_\w/, running through it you can get each group and then recreate your JSON tree – Nick.T Nov 27 '12 at 15:22
show 1 more comment

1 Answer

up vote 2 down vote accepted

Well, besides the obvious fact that this entire structure needs to be rethought and borders on schizophrenic, the answer is pretty straightforward.

Here's a jsfiddle that works. Essentially you just want to accumulate object, checking at each level, over the key elements.

function build(attr) {
    var target, key, out = {},
        i = -1,
        len = attr.length;
    while (++i < len) {
        key = attr[i].id.split('.');
        target = makeOrFind(out, key);
        extend(target, attr[i]);
    }
    return out;
}

function makeOrFind(target, keyParts) {
    var key, i = -1, len = keyParts.length;
    while(++i < len) {
       key = keyParts[i];
        if( i > 0 ) {
           if( !target.nestedItems ) { target.nestedItems = {}; }
           target = target.nestedItems;
        }
       if( !target[key] ) { target[key] = {}; }
       target = target[key];
    }
    return target;
}

function extend(target, vals) {
   // this is an overly simple implementation of extend
   // it only works if your objects are exactly one level deep
   // but extend is a solved problem and not really in the scope of the question
    for( var key in vals ) {
        if( vals.hasOwnProperty(key) ) {
            target[key] = vals[key];   
        }
    }        
}

var out = build(attr);
console.log(out);

share|improve this answer
I see that you've changed the desired output slightly. Sadly, my good programming sense won't allow me to randomly wrap every object in an array just because I can; it hurts my little brain cells (and they are little). I'm sure it won't take much thought to work that out, though. – Kato Nov 27 '12 at 15:45
This is perfect. Thank you so much. Now to dive in and really understand the above code. – Seth Nov 27 '12 at 15:52
If you have questions, shoot me an email. The address is katowulf and I'm on gmail. – Kato Nov 27 '12 at 15:57

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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