traversing through JSON string to inner levels using recursive function
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']
user1371896
Updated on October 30, 2020Comments
-
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 about 12 yearshow do I add the value of subobj here for the above example?
-
Florian Margaine about 12 yearsWhat? I don't understand your comment.
-
Florian Margaine about 12 yearsOops, edited my answer correcly. The
subobj
is the property of the object that is an object, so you can traverse it too. -
user1371896 about 12 yearsone 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 about 12 yearsI'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 about 12 yearsyes , 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 about 12 yearsOh, I guess you want:
h.Categories.Facets[0].''.entity += Company[0]
-
El Ronnoco about 12 yearsI've answered this on my iPhone so if someone could fix my formatting i'd be very grateful!!!
-
user1371896 about 12 yearsits showing the error,if is nt defined for If (h.hasOwnProperty(k))
-
El Ronnoco about 12 yearsAh you may new to test for 'if (h instanceof object)' before entering the loop.
-
user1371896 about 12 yearsno,,,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 about 12 yearsSorry 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 about 12 yearsI 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 about 12 yearsI'm sorry, but I really don't understand what you mean.
-
Florian Margaine about 12 yearsThe "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 usehasOwnProperty
. -
El Ronnoco about 12 yearsHow 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 about 12 yearsThat's unusual, are you running in firefox? Any JS engine should easily handle 3 levels deep!...
-
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 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 about 12 yearsYou 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 about 12 yearsIve 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 about 12 yearsSay if (k==='entity') alert( obj.entity ); This should only alert with entity values...
-
user1371896 about 12 yearsThat 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..