3

I have the following JSON with "section" objects containing other "section" objects and "items". From this object, i need to extract the "items" field name (concatenation of all the parent sections and its name attribute") and its value. What is the efficient way to do this?

Please note, the following is just a part of my json. Nesting levels of sections and items are unlimited. I need to do something recursively.

JSON:

{
    "section": [
        {
            "fieldId": "t1",
            "name": "section-1",
            "section": [
                {
                    "fieldId": "t-1",
                    "name": "section2",
                    "and": [
                        {
                            "fieldId": null,
                            "items": [
                                {
                                    "fieldId": "c-2",
                                    "name": "item1",
                                    "value": "123"
                                },
                                {
                                    "fieldId": "c-3",
                                    "name": "item2",
                                    "value": "dsadsaddsa"
                                }
                            ]
                        }
                    ],
                    "section": []
                }
            ]
        }
    ]
}

Javascript I started writing (not complete, I am stuck):

function process(key,value) {
    if (key == "section") 
        console.log(key + " : " + JSON.stringify(value[0]));
}

function traverse(o,func) {
    for (var i in o) {
        func.apply(this,[i,o[i]]);        
        if (typeof(o[i])=="object") {
            traverse(o[i],func);
        }
    }
}

Expected output: A javascript object

{"section1-section2-item1":123, "section1-section2-item2":"dsadsaddsa"}
7
  • Do you need this ultra generic or can you rely on section and items? The latter would be much more efficient. Commented Aug 12, 2013 at 17:08
  • I can rely on "section" and "items". Thanks. Commented Aug 12, 2013 at 17:13
  • Where exactly did you get stuck? Did you write that traverse function yourself, if seems quite abstract? How do you call it? Commented Aug 12, 2013 at 17:49
  • @M99: So the array in the and property (in your example) can be ignored? Commented Aug 12, 2013 at 17:51
  • Yes. "And" can be ignored. Commented Aug 12, 2013 at 18:34

1 Answer 1

2

Here is a working version of a recursive traversal function:

It is verbose, but reasonably efficient. It does do some extra traversal/work with the defaults as they are - that can be cleaned up as well if it is a concern.

Updated to collect key/value parse as they are processed and log the final result.

Working Fiddle

// This is just to help visualize the answer in the fiddle
function log(input) {
    $('body').append($('<div>').text(input));
}

var finalObj = {};
function process(key,value) {
    log(key + " : " + value);

    // Collect in finalObj
    finalObj[key] = value;
}


function traverse(section,func,path) {
    var sectionIdx, andArr, andIdx, curAnd, items, itemIdx, curSection, curPath;

    section = section || [];
    path    = path || [];

    // Start with something in the path for this level
    path.push('');

    for(sectionIdx = 0; sectionIdx < section.length; ++sectionIdx) {
        // Set reasonable defaults for current values
        curSection = section[sectionIdx] || {};
        andArr     = curSection.and || [];

        // Update the name if the section for this level in the hierarchy
        path[path.length - 1] = curSection.name;

        // Recurse into the current section
        traverse(curSection.section, func, path);

        // Remove the last part of the path from the previous recursion
        path.length = path.length - 1;

        // Current path as a string
        curPath = path.join('-'); 

        // Now iterate all the and objects
        for(andIdx = 0; andIdx < andArr.length; ++andIdx) {
            // Setup reasonable defaults for and and items
            curAnd = andArr[andIdx] || {};
            items  = curAnd.items || [];

            // Run through the items and process them with the full path
            for(itemsIdx = 0; itemsIdx < items.length; ++itemsIdx) {
                process(curPath + '-' + items[itemsIdx].name, items[itemsIdx].value);
            }
        }
    }
}

traverse(jsonInput.section, process);

// Log out finalObj
log("Final Result:");
log(JSON.stringify(finalObj));

Output (based on the current name properties in the provided JSON):

section-1-section2-item1 : 123
section-1-section2-item2 : dsadsaddsa
Final Result:
{"section-1-section2-item1":"123","section-1-section2-item2":"dsadsaddsa"}

Updated fiddle that avoids the extra recursion when there is no child section array

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your awesome and very quick solution. What do you mean by "extra traversal/work with the defaults as they are - that can be cleaned up"?
One example is curSection. The code will always recurse passing curSection even if it had been set to an empty object. The alternative is more inline checks/conditional logic to avoid some of the extra function calls. I felt this approach was a bit clearer/easier to read.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.