Reputation: 357
I need a javascript recursive function that return an array of values when key and JavaScript object is passed to this function.
Note that the nested JavaScript object has unknown depth. The function is working fine but it returns duplicates.
function getValuesByKey(object, key) {
var values = [];
recursiveFx(object);
function recursiveFx(object) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
if (typeof object[property] == "object") {
recursiveFx(object[property]);
} else {
//found a property which is not an object
try {
if (isDefined(object[key])) {
console.log('Here is the value that is to be pushed',object[key]);
values.push(object[key]);
}
} catch (e) {
}
}
}
}
}
return values;
}
Here is the isDefined helper function
function isDefined(variable) {
try {
if (typeof(variable) !== 'undefined') return true;
} catch (e) {
return false;
}
}
Here is an example of JavaScript object:
{
"children": [{
"id": "5",
"parentid": "0",
"text": "Device Guides",
"index": "1",
"children": [{
"id": "10",
"index": "0",
"text": "Grandstream GXP-21XX"
}, {
"id": "11",
"index": "1",
"text": "Polycom Soundstation/Soundpoint"
}, {
"id": "23",
"parentid": "8",
"index": "2",
"text": "New Polycom",
"children": [{
"id": "5",
"parentid": "0",
"text": "Device Guides",
"index": "1",
"children": [{
"id": "10",
"index": "0",
"text": "Grandstream GXP-21XX"
}, {
"id": "11",
"index": "1",
"text": "Polycom Soundstation/Soundpoint"
}, {
"id": "23",
"index": "2",
"text": "New Polycom"
}]
}, {
"id": "6",
"parentid": "0",
"text": "Pre-Sales Evaluation",
"index": "0",
"children": []
}, {
"id": "7",
"parentid": "0",
"text": "Router Setup Guides",
"index": "2",
"children": [{
"id": "9",
"index": "0",
"text": "Sonicwall"
}, {
"id": "12",
"index": "1",
"text": "Cisco"
}]
}]
}, {
"id": "6",
"parentid": "0",
"text": "Pre-Sales Evaluation",
"index": "0",
"children": []
}, {
"id": "7",
"parentid": "0",
"text": "Router Setup Guides",
"index": "2",
"children": [{
"id": "9",
"index": "0",
"text": "Sonicwall"
}, {
"id": "12",
"index": "1",
"text": "Cisco"
}]
}]}]};
When I run this getValuesByKey(jsonObj, 'id');
I get the following array:
["5", "5", "5", "5", "10", "10", "10", "11", "11", "11", "23", "23", "23", "23", "5", "5", "5", "5", "10", "10", "10", "11", "11", "11", "23", "23", "23", "6", "6", "6", "6", "7", "7", "7", "7", "9", "9", "9", "12", "12", "12", "6", "6", "6", "6", "7", "7", "7", "7", "9", "9", "9", "12", "12", "12"]
Notice that 5 has been returned 4 times instead of 2 times
Upvotes: 3
Views: 12471
Reputation:
You are checking for the presence of key
each time through the loop over the object's properties. So you are getting as many values as there are properties on the object. So:
function getValuesByKey(object, key) {
var values = [];
recursiveFx(object);
function recursiveFx(object) {
if (key in object) values.push(object[key]);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
for (var property in object) {
if (object.hasOwnProperty(property)) {
if (typeof object[property] == "object") {
recursiveFx(object[property]);
}
}
}
}
return values;
}
JSON.stringify
with replacerAnyway, you can do this more easily with
function getValuesByKey(object, key) {
var values = [];
JSON.stringify(object, function(k, v) {
if (k === key) values.push(v);
return v;
});
return values;
}
This uses the replacer
parameter to JSON.stringify
to intercept each key value pair. The stringified value itself we don't need and throw away.
Upvotes: 11
Reputation: 386654
Just an idea of recursion:
var data = { "children": [{ "id": "5", "parentid": "0", "text": "Device Guides", "index": "1", "children": [{ "id": "10", "index": "0", "text": "Grandstream GXP-21XX" }, { "id": "11", "index": "1", "text": "Polycom Soundstation/Soundpoint" }, { "id": "23", "parentid": "8", "index": "2", "text": "New Polycom", "children": [{ "id": "5", "parentid": "0", "text": "Device Guides", "index": "1", "children": [{ "id": "10", "index": "0", "text": "Grandstream GXP-21XX" }, { "id": "11", "index": "1", "text": "Polycom Soundstation/Soundpoint" }, { "id": "23", "index": "2", "text": "New Polycom" }] }, { "id": "6", "parentid": "0", "text": "Pre-Sales Evaluation", "index": "0", "children": [] }, { "id": "7", "parentid": "0", "text": "Router Setup Guides", "index": "2", "children": [{ "id": "9", "index": "0", "text": "Sonicwall" }, { "id": "12", "index": "1", "text": "Cisco" }] }] }, { "id": "6", "parentid": "0", "text": "Pre-Sales Evaluation", "index": "0", "children": [] }, { "id": "7", "parentid": "0", "text": "Router Setup Guides", "index": "2", "children": [{ "id": "9", "index": "0", "text": "Sonicwall" }, { "id": "12", "index": "1", "text": "Cisco" }] }] }] };
function getValuesByKey(object, key) {
var values = [];
function r(obj) {
Object.keys(obj).forEach(function (k) {
if (Array.isArray(obj[k])) {
obj[k].forEach(r);
return;
}
if (typeof obj[k] === 'object') {
r(obj[k]);
return;
}
k === key && !~values.indexOf(obj[k]) && values.push(obj[k]);
});
}
r(object);
return values;
}
document.write('<pre>' + JSON.stringify(getValuesByKey(data, 'id'), 0, 4) + '</pre>');
Upvotes: 3
Reputation: 932
Ok I got it, there is a bug in your code. You should test that key === property when testing if (isDefined(object[key])) {
Otherwise, you're just adding the matching value whenever you're examining an object that has that value in another property (if that's clear :))
Upvotes: 0