traversing through JSON string to inner levels using recursive function

38,412

Solution 1

I have made a jsfiddle which traverses every object,array and value in the JS object like so...

function scan(obj) {
    var k;
    if (obj instanceof Object) {
        for (k in obj){
            if (obj.hasOwnProperty(k)){
                //recursive call to scan property
                scan( obj[k] );  
            }                
        }
    } else {
        //obj is not an instance of Object so obj here is a value
    };

};

I get no recursion error (in Chrome). Can you use this to do what you want?

If you need to test if an object is an array use if (obj instanceof Array)

To test if an object has an "entity" property use if (obj.hasOwnProperty('entity'))

To add (or modify an existing) "entity" property use obj.entity = value or obj['entity'] = value

Solution 2

Say I have a structure like the following:

var aObject = {
    items: [],
    children: {}
}

Children is an associative array that contains more aObjects. So it could look like this:

var aObject = {
    items: [],
    children: {
        "subgroup1": {
            items: [],
            children: {}
        },
        "subgroup2": {
            items: [],
            children: {}
        }
    }
}

I have an item that contains an array of subgroups:

["subgroup1", "subgroup1a"]

Each subgroup is a 'location'. The item needs to be placed at:

aObject.children[array[0]].children[array[1]].items

At each level, we have to check if children[array[i]] exists, and if not, create it. You can't simply write aObject.children[array[0]].children[array[1]].items.push(item) because children[array[0]] might not already exist and we will get an error.

This can be solved using recursion! (AngularJS)

function recursive(aLevel, aItem, aArray, aIndex){
    var lLevel = aLevel;

    // If we have reached the end of the array
    if (aIndex === aArray.length){
        // Insert
        aLevel.items.push(aItem);
    } else {

        // If the subgroup doesn't exist, create it
        if (typeof aLevel.children[aArray[aIndex]] === 'undefined'){
            aLevel.children[aArray[aIndex]] = {
              items: [],
              children: {}
            };
        }

        // Move into
        recursive(aLevel.children[aArray[aIndex]], aItem, aArray, aIndex+1);
    }
}

aObject = {
    items: [],
    children: {},
}

angular.forEach(items, function(item, i){
    var location = item.location;

    if (location.length == 0){
        aObject.items.push(item);
    } else {
        recursive(aObject, item, location, 0);
    }
});

The final aObject would look like this:

var aObject = {
     items: [],
     children: {
        "subgroup1": {
            items: [],
            children: {
                "subgroup1a": {
                    items: [item],
                    children: {}
                }
            }
        },
        "subgroup2": {
            items: [],
            children: {}
        }
    }
}

Solution 3

(function recur( obj ) {
    Object.keys( obj ).forEach( function( prop ) {
        // Check if the property is an object
        if ( ({}).toString.apply( prop ) === '[object Object]' ) {
            // If it is, recall this function
            recur( prop );
        }
    } );
} () );

I haven't added your logic, but you get the idea of how to recursively traverse your object.

Solution 4

Here is a function that i use often. It's easily modifiable to do many recursive tasks. For example if you add a bail flag you can quickly get the stack or add a callback function that makes it even more general. Anyway that's my 2 cents

var recursiveObjMap = (function(){
  var stack = [];
  var result = [];
  // var bail = false;
  return function map(data, key){
    if (!$.isArray(data) && !$.isPlainObject(data) ) { 
      result.push(data);
      return false 
    }

    $.each(data, function(i, v){
      if (key) stack.push(key);
      map(v, i);
      stack.pop();
    });
    return result;
  };
})();

recursiveObjMap({a:'b',c:{d:{e:"f"}}}) // ['b', 'f']
Share:
38,412
user1371896
Author by

user1371896

Updated on October 30, 2020

Comments

  • user1371896
    user1371896 over 3 years

    I have a JSON input which can go to any number of levels.

    I'm giving an input sample of

    var d=getEntities( {"Categories": 
    {
    "Facets": 
        [
        {
        "count": 1,
        "entity": "Company",
        "Company": 
                [
                {
    
                "entity": "Ford Motor Co",
    
                "Ford_Motor_Co": 
                    [
                        {
                        "count": 1,
                        "entity": "Ford"
                        }
                    ]
                }
                ]
        },
            {
                "count": 4,
                "entity": "Country",
                  "Country": [
                        {
    
                            "entity": "Germany",
                             "Germany": [
                                    {
                                        "count": 1,
                                        "entity": "Germany"
                                    }
                              ],
                            "currency": "Euro (EUR)"
                        },
                        {
    
                             "entity": "Italy",
                            "Italy": [
                                    {
                                         "count": 1,
                                         "entity": "Italy"
                                    }
                              ],
                            "currency": "Euro (EUR)"
                        },
                        {
    
                            "entity": "Japan",
                              "Japan": [
                                 {
                                        "count": 1,
                                        "entity": "Japan"
                                 }
                              ],
                            "currency": "Yen (JPY)"
                        },
                        {
    
                            "entity": "South Korea",
                              "South_Korea": [
                                  {
                                        "count": 1,
                                        "entity": "South Korea"
                                    }
                              ],
                          "currency": "Won (KRW)"
                        }
                  ]
            },
            {"count": 5,
                  "entity": "Persons",
                  "Persons": [
                        {
                             "count": 2,
                            "entity": "Dodge"
                        },
                        {
                            "count": 1,
                            "entity": "Dodge Avenger"
                        },
                        {
                            "count": 1,
                            "entity": "Major League"
                        },
                        {
                            "count": 1,
                            "entity": "Sterling Heights"
                        }
                  ]
            }
      ]
    
    }});
    

    I want to add the key value "Entity" in all levels to an array using recursion,

    I'm able to collect the data from first level using the string

    <html>
    <head>
    <script src="jquery.js" type="text/javascript"></script>
    <script type="text/javascript" src="dataDumper.js"></script>
    
    
    <script type="text/javascript">
    
    var testJSON = {"Categories": 
    {
    "Facets": 
        [
        {
        "count": 1,
        "entity": "Company",
        "Company": 
                [
                {
    
                "entity": "Ford Motor Co",
    
                "Ford_Motor_Co": 
                    [
                        {
                        "count": 1,
                        "entity": "Ford"
                        }
                    ]
                }
                ]
        },
            {
                "count": 4,
                "entity": "Country",
                  "Country": [
                        {
    
                            "entity": "Germany",
                             "Germany": [
                                    {
                                        "count": 1,
                                        "entity": "Germany"
                                    }
                              ],
                            "currency": "Euro (EUR)"
                        },
                        {
    
                             "entity": "Italy",
                            "Italy": [
                                    {
                                         "count": 1,
                                         "entity": "Italy"
                                    }
                              ],
                            "currency": "Euro (EUR)"
                        },
                        {
    
                            "entity": "Japan",
                              "Japan": [
                                 {
                                        "count": 1,
                                        "entity": "Japan"
                                 }
                              ],
                            "currency": "Yen (JPY)"
                        },
                        {
    
                            "entity": "South Korea",
                              "South_Korea": [
                                  {
                                        "count": 1,
                                        "entity": "South Korea"
                                    }
                              ],
                          "currency": "Won (KRW)"
                        }
                  ]
            },
            {"count": 5,
                  "entity": "Persons",
                  "Persons": [
                        {
                             "count": 2,
                            "entity": "Dodge"
                        },
                        {
                            "count": 1,
                            "entity": "Dodge Avenger"
                        },
                        {
                            "count": 1,
                            "entity": "Major League"
                        },
                        {
                            "count": 1,
                            "entity": "Sterling Heights"
                        }
                  ]
            }
      ]
    
    }};
    
    function scan(obj)
    {
        var k;
        if (obj.hasOwnProperty('entity')) {
    
    
    
            for (k in obj){
               if (obj.hasOwnProperty(k)){
    
    
                    scan( obj[k] );  
    
    
                }                
              }
        } 
    
    
        else{
            if(k=='entity')
            {
            alert(obj.entity);
       }
        }
    
    
    };
    
    scan(testJSON);
    
    
    
    </script>
    </head>
    
    <body>
    
    </body>
    
    </html>
    

    How do I get in to the inner levels for JSON string using recursive functions?

  • user1371896
    user1371896 about 12 years
    how do I add the value of subobj here for the above example?
  • Florian Margaine
    Florian Margaine about 12 years
    What? I don't understand your comment.
  • Florian Margaine
    Florian Margaine about 12 years
    Oops, edited my answer correcly. The subobj is the property of the object that is an object, so you can traverse it too.
  • user1371896
    user1371896 about 12 years
    one more doubt, for recursion for this doubt. what If I want to get the value Ford Motor Co, I can do it this way h.Categories.Facets[0].Company[0].entity or I take the value Company using h.Categories.Facets[0].entity and then how do I append the value Company in to "h.Categories.Facets[[0]. '()' .entity" line..
  • Florian Margaine
    Florian Margaine about 12 years
    I'm not sure I got you, but are you looking for: h.Categories.Facets[[0]. '()' .entity = h.Categories.Facets[0].Company[0].entity? The first part is clearly not good, but I don't know what you mean.
  • user1371896
    user1371896 about 12 years
    yes , how do I concatinate the value Company[0] in to h.Categories.Facets[0].''.entity .. Will string concatination wrk?? Like var s=h.Categories.Facets[0].entity; and then h.Categories.Facets[0].s[0].entity or h.Categories.Facets[0].s.entity
  • Florian Margaine
    Florian Margaine about 12 years
    Oh, I guess you want: h.Categories.Facets[0].''.entity += Company[0]
  • El Ronnoco
    El Ronnoco about 12 years
    I've answered this on my iPhone so if someone could fix my formatting i'd be very grateful!!!
  • user1371896
    user1371896 about 12 years
    its showing the error,if is nt defined for If (h.hasOwnProperty(k))
  • El Ronnoco
    El Ronnoco about 12 years
    Ah you may new to test for 'if (h instanceof object)' before entering the loop.
  • user1371896
    user1371896 about 12 years
    no,,,it ws the format of ur code...I was in big letter.. It now alerts first entity Company and then shows "too much recursion " for getEntities(h[k]);
  • El Ronnoco
    El Ronnoco about 12 years
    Sorry I'm using a phone! Yes but the routine will iterate through every level, you will need to add logic to restrict recursion to meet your requirement. I didn't fully understand your question and I can't see your Json properly.
  • user1371896
    user1371896 about 12 years
    I would be realy grateful if u cud help me later with this thing.. I will try to find a stop expression to restrict the recursion.
  • Florian Margaine
    Florian Margaine about 12 years
    I'm sorry, but I really don't understand what you mean.
  • Florian Margaine
    Florian Margaine about 12 years
    The "stop expression" is the one I used in my answer: if ( ({}).toString.apply( prop ) === '[object Object]' ) {. Btw, Object.keys returns an array of the enumerable properties, so it is not needed to use hasOwnProperty.
  • El Ronnoco
    El Ronnoco about 12 years
    How deep is your object nested? I'm not sure how many levels of recursion JS supports. @florian that's a good tip thanks.
  • El Ronnoco
    El Ronnoco about 12 years
    That's unusual, are you running in firefox? Any JS engine should easily handle 3 levels deep!...
  • user1371896
    user1371896 about 12 years
    @ElRonnoco Ive edited the question part to add the program that Ive wrote... You can check out if its wrking by copying it and saving as html file..
  • user1371896
    user1371896 about 12 years
    @ElRonnoco This will solve my prblem, thanks a lot!!! bt one last thing... I need only the values of Entity key, so I tried if (obj.hasOwnProperty('entity')) instead of if (obj.hasOwnProperty(k)), bt it dint wrk!!
  • El Ronnoco
    El Ronnoco about 12 years
    You will still need the scan function to be called for each property, you just want to only do something when it's 'entity'. See how you get on with if (k==='entity') within the for loop...
  • user1371896
    user1371896 about 12 years
    Ive used the condition if(k==entity), bt it will give me all the values, like count, currrency everything...Hw cme I cn take only values with key "entity"??
  • El Ronnoco
    El Ronnoco about 12 years
    Say if (k==='entity') alert( obj.entity ); This should only alert with entity values...
  • user1371896
    user1371896 about 12 years
    That will not do the trick.. it wont enter the if I add the condition.. Will edit the part html part in question to show u..