Reputation: 18875
I wrote the following function to traverse a JSON-structured data to locate the value of the provided targetField
and the paths pathArr
, but I am struggling with returning result from the recursion, because the value for the targetField may not exist.
This function uses a global variable named result for returning the targetField's value, but this can get carried over to the next call of findValue(), which may not find the value but shows the last call's result
. So I am wondering what's the best way of solving this problem without using the global variable result
.
If the call of findValue() does not find the targetField's value, just return null as the result.
The pathArr is something like ["first_level", "second_level", "third_level"];
var result = null;
function findValue(jsonData, pathArr, targetField) {
if (pathArr.length == 0) {
result = targetField in jsonData ? jsonData[targetField] : result;
return result;
} else {
var curNode = pathArr.shift();
if (curNode in jsonData) {
jsonData = jsonData[curNode];
}
if (Array.isArray(jsonData)) {
jsonData.forEach(function(thisData) {
findValue(thisData, pathArr, targetField);
});
} else {
findValue(jsonData, pathArr, targetField);
}
}
return result;
}
Edit: Thanks to everyone who replied.
Using unobf's improved code, I used this testData to do a testing, but it seems only returning the last match:
var testData = {
"num_found" : 3,
"category" : "social",
"groups": [
{"group" : {
"source" : [{"id" : "testID1", "num": 10, "field": "sociaology", "sub-subject" : "socialeconomy"},
{"id" : "testID2", "num": 20, "field": "mathematics", "sub-subject": ""},
{"id" : "testID3", "num": 7, "field": "biology", "sub-subject" : ""}
],
"identifier" : "shelf-01-E-XW1"
}},
{"group" : {
"source" : [{"id" : "testID4", "num": 50, "field": "sociaology2", "sub-subject" : ""},
{"id" : "testID5", "num": 44, "field": "mathematics2", "sub-subject": ""},
{"id" : "testID6", "num": 75, "field": "biology2", "sub-subject" : "european studies2"}
],
"identifier" : "shelf-02-W-EW3"
}},
{"group" : {
"source" : [{"id" : "testID7", "num": 59, "field": "sociaology3", "sub-subject" : "socialeconomy3"},
{"id" : "testID8", "num": 47, "field": "mathematics3", "sub-subject": ""},
{"id" : "testID9", "num": 76, "field": "biology3", "sub-subject" : "european studies3"}
],
"identifier" : "shelf-03-W-GW5"
}}
]
};
and I found I had to change this line of code
return {value : result};
to
return result;
Test:
for (var i = 0; i < testData.groups.length; i++) {
var value = findValue(testData.groups[i], ["group", "source"], "sub-subject");
console.log("found value: " + value);
}
Upvotes: 0
Views: 122
Reputation: 7244
Here is the solution with some test data
function findValue(jsonData, pathArr, targetField) {
var result = null;
if (pathArr.length == 0 && jsonData.hasOwnProperty(targetField)) {
result = targetField in jsonData ? jsonData[targetField] : result;
return { value : result};
} else {
jsonData = jsonData[pathArr.shift()]
if (Array.isArray(jsonData)) {
jsonData.forEach(function(thisData) {
result = findValue(thisData, pathArr, targetField);
});
} else if (typeof jsonData !== 'undefined') {
return findValue(jsonData, pathArr, targetField);
}
}
return result;
}
testData = [{
hello : 'hello',
pathArr: []
}, {
child : {
hello : false
},
pathArr: ['child']
}, {
child : {
hello : null
},
pathArr: ['child']
}, {
child: {
child: {
hell: 'hell'
}
},
pathArr: ['child', 'child']
}];
testData.forEach(function (json) {
console.log(findValue(json, json.pathArr, 'hello'));
});
Upvotes: 1
Reputation: 1074088
Fundamentally, all of the paths through your function must return a value. Currently, there's a path that doesn't, the path that takes the forEach
:
jsonData.forEach(function(thisData) {
findValue(thisData, pathArr, targetField);
});
There, you probably want some
(so you can stop when you find it) and a variable that you can set to the found value, e.g:
var retval;
and
jsonData.some(function(thisData) {
retVal = findValue(thisData, pathArr, targetField);
return !!retVal; // Stops the `some` loop, doesn't exit `findValue`
});
if (retVal) {
return retVal;
}
Side note: You don't want to declare result
outside the function, there's no need. The function can be entirely self-contained. Here's a version that is self-contained, for instance:
function findValue(jsonData, pathArr, targetField) {
var result = null;
if (pathArr.length == 0) {
result = targetField in jsonData ? jsonData[targetField] : null;
} else {
var curNode = pathArr.shift();
if (curNode in jsonData) {
jsonData = jsonData[curNode];
}
if (Array.isArray(jsonData)) {
jsonData.some(function(thisData) {
result = findValue(thisData, pathArr, targetField);
return !!result; // Stops the `some` loop, doesn't exit `findValue`
});
} else {
result = findValue(jsonData, pathArr, targetField);
}
}
return result;
}
Note: I didn't do a full review of the code, just looked at return values.
Upvotes: 1