I have this json object
{
"data": {
"user": {
"user_info": {
"id": "AoGC2HQ9vedHmzcMX"
},
"product": [
{
"node": {
"id": "NzcxNzU2ODU1ODM1",
"feedback": {
"raters": {
"nodes": [
{
"id": "1",
"name": "Dan"
},
{
"id": "2",
"name": "Allen"
},
{
"id": "3",
"name": "Williams"
}
]
},
"commentors": {
"nodes": [
{
"id": "001",
"name": "Kent"
},
{
"id": "002",
"name": "Jay"
}
]
}
}
}
}
]
}
}
}
So how do I make it to get values of id If the parent property matches the desired key name, In this example I want to get all id's from raters.nodes only.
so expected result is
[1,2,3]
I know can do obj.data.user.product[0].node.feedback.raters.nodes and loop through that, but that is not how I want and the object tree occasionally changes.
I have used this recursive function
const recursiveSearch = (obj, searchKey, results = []) => {
const r = results;
Object.keys(obj).forEach(key => {
const value = obj[key];
if(key === searchKey && typeof value !== 'object'){
r.push(value);
}else if(typeof value === 'object'){
recursiveSearch(value, searchKey, r);
}
});
return r;
};
//returns all id's
While it works, it returns all id values, so how do I improve it? If not, how do I make this possible?
I think you want to really do this in 2 steps,..
First make a function to get the root node your looking for, and then you can just use map like normal.
Below is an example.
var data = JSON.parse("{\"data\":{\"user\":{\"user_info\":{\"id\":\"AoGC2HQ9vedHmzcMX\"},\"product\":[{\"node\":{\"id\":\"NzcxNzU2ODU1ODM1\",\"feedback\":{\"raters\":{\"nodes\":[{\"id\":\"1\",\"name\":\"Dan\"},{\"id\":\"2\",\"name\":\"Allen\"},{\"id\":\"3\",\"name\":\"Williams\"}]},\"commentors\":{\"nodes\":[{\"id\":\"001\",\"name\":\"Kent\"},{\"id\":\"002\",\"name\":\"Jay\"}]}}}}]}}}");
function getRoot(data, search) {
function get(path, data) {
for (const [k, v] of Object.entries(data)) {
if (v instanceof Object) {
const pp = `${path}.${k}`;
if (pp.slice(-search.length) === search) {
return v;
}
const r = get(`${path}.${k}`, v);
if (r) return r;
}
}
}
return get('', data);
}
const r = getRoot(data, 'raters.nodes');
console.log(r && r.map(i => i.id));
So, i have this json file, in which i have to take out the fileName tag, and use it.
{
"dataset": {
"private": false,
"stdyDscr": {
"citation": {
"titlStmt": {
"titl": "Smoke test",
"IDNo": {
"text": "10.5072/FK2/WNCZ16",
".attrs": {
"agency": "doi"
}
}
},
"rspStmt": {
"AuthEnty": "Dataverse, Admin"
},
"biblCit": "Dataverse, Admin, 2015, \"Smoke test\", http://dx.doi.org/10.5072/FK2/WNCZ16, Root Dataverse, V1 [UNF:6:iuFERYJSwTaovVDvwBwsxQ==]"
}
},
"fileDscr": {
"fileTxt": {
"fileName": "fearonLaitinData.tab",
"dimensns": {
"caseQnty": "6610",
"varQnty": "69"
},
"fileType": "text/tab-separated-values"
},
"notes": {
"text": "UNF:6:K5wLrMhjKoNX7znhVpU8lg==",
".attrs": {
"level": "file",
"type": "VDC:UNF",
"subject": "Universal Numeric Fingerprint"
}
},
".attrs": {
"ID": "f6"
}
}
},
im using d3.js mostly, but some parts of jquery and javascript with it. right now im doing:
d3.json(url,function(json){
var jsondata=json;
var temp = jsondata.dataset.fileDscr.fileTxt.fileName;
}
Is there a way to just access fileName directly? Im asking because, i have to make this generic to fit other json files, where the nesting might be different.
This will return the value for some instance of the key in the JSON data, if it exists.
var data = {...};
function findValue(json, key) {
if (key in json) return json[key];
else {
var otherValue;
for (var otherKey in json) {
if (json[otherKey] && json[otherKey].constructor === Object) {
otherValue = findValue(json[otherKey], key);
if (otherValue !== undefined) return otherValue;
}
}
}
}
console.log(findValue(data, 'fileName'));
It will return a comma separated string of all the values of a specified key
function walk(obj,keyname) {
var propertyVal="";
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var val = obj[key];
if(typeof(val) == 'object') {
console.log(val);
propertyVal+= walk(val,keyname);
}else {
if(key == keyname){
propertyVal = propertyVal+","+obj[key];
}
}
}
}
return propertyVal;
}
alert(walk(data,'filename').replace(',',''));
I am having below object where I am trying to get all the id values.
[{
"type": "test",
"id": "100",
"values": {
"name": "Alpha"
},
"validations": []
}, {
"type": "services",
"validations": [{
"id": "200",
"name": "John",
"selection": [{
"id": "300",
"values": {
"name": "Blob"
}
}]
}]
}]
Using the below code, I am getting only the first id value. Is there any way to get all the id values from the nested object without using any external module.
for (var prop in obj) {
console.log(prop)
if (prop === key) {
set.push(prop);
}
}
Expected Output
[100,200,300] //all id values
You can use a JavaScript function like below to get the nested properties:
function findProp(obj, key, out) {
var i,
proto = Object.prototype,
ts = proto.toString,
hasOwn = proto.hasOwnProperty.bind(obj);
if ('[object Array]' !== ts.call(out)) out = [];
for (i in obj) {
if (hasOwn(i)) {
if (i === key) {
out.push(obj[i]);
} else if ('[object Array]' === ts.call(obj[i]) || '[object Object]' === ts.call(obj[i])) {
findProp(obj[i], key, out);
}
}
}
return out;
}
Check this Fiddle for a working solution.
Using Object.keys
function findProp(obj, prop) {
var result = [];
function recursivelyFindProp(o, keyToBeFound) {
Object.keys(o).forEach(function (key) {
if (typeof o[key] === 'object') {
recursivelyFindProp(o[key], keyToBeFound);
} else {
if (key === keyToBeFound) result.push(o[key]);
}
});
}
recursivelyFindProp(obj, prop);
return result;
}
// Testing:
var arr = [{
"type": "test",
"id": "100",
"values": {
"name": "Alpha"
},
"validations": []
}, {
"type": "services",
"validations": [{
"id": "200",
"name": "John",
"selection": [{
"id": "300",
"values": {
"name": "Blob"
}
}]
}]
}];
console.log(findProp(arr, "id"));
To get the keys from nested objects, you first need to put your code in a function, then for each of the top-level keys, check if it's an array or object. If it is, just call your function again from within that function (weird, I know.) Just make sure you don't skip the check of whether it's an object. You'll get stuck in an infinite loop. Something like this:
function parseObjectKeys(obj) {
for (var prop in obj) {
console.log(prop)
var sub = obj[prop]
if (typeof(sub) == "object") {
parseObjectKeys(sub);
}
}
}
Here's a more complex example:
https://jsfiddle.net/tfqLnzLm/1/
You can use a XPath styled json parser like JSONPath. The version I'm presenting here is a extended version I did here:
function jsonPath(obj,expr,arg){var P={resultType:arg&&arg.resultType||"VALUE",result:[],normalize:function(e){var t=[];return e.replace(/[\['](\??\(.*?\))[\]']/g,function(e,r){return"[#"+(t.push(r)-1)+"]"}).replace(/'?\.'?|\['?/g,";").replace(/;;;|;;/g,";..;").replace(/;$|'?\]|'$/g,"").replace(/#([0-9]+)/g,function(e,r){return t[r]})},asPath:function(e){for(var t=e.split(";"),r="$",a=1,n=t.length;n>a;a++)r+=/^[0-9*]+$/.test(t[a])?"["+t[a]+"]":"['"+t[a]+"']";return r},store:function(e,t){return e&&(P.result[P.result.length]="PATH"==P.resultType?P.asPath(e):t),!!e},trace:function(e,t,r){if(e){var a=e.split(";"),n=a.shift();if(a=a.join(";"),t&&t.hasOwnProperty(n))P.trace(a,t[n],r+";"+n);else if("*"===n)P.walk(n,a,t,r,function(e,t,r,a,n){P.trace(e+";"+r,a,n)});else if(".."===n)P.trace(a,t,r),P.walk(n,a,t,r,function(e,t,r,a,n){"object"==typeof a[e]&&P.trace("..;"+r,a[e],n+";"+e)});else if(/,/.test(n))for(var l=n.split(/'?,'?/),s=0,c=l.length;c>s;s++)P.trace(l[s]+";"+a,t,r);else/^\(.*?\)$/.test(n)?P.trace(P.eval(n,t,r.substr(r.lastIndexOf(";")+1))+";"+a,t,r):/^\?\(.*?\)$/.test(n)?P.walk(n,a,t,r,function(e,t,r,a,n){P.eval(t.replace(/^\?\((.*?)\)$/,"$1"),a[e],e)&&P.trace(e+";"+r,a,n)}):/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(n)&&P.slice(n,a,t,r)}else P.store(r,t)},walk:function(e,t,r,a,n){if(r instanceof Array)for(var l=0,s=r.length;s>l;l++)l in r&&n(l,e,t,r,a);else if("object"==typeof r)for(var c in r)r.hasOwnProperty(c)&&n(c,e,t,r,a)},slice:function(e,t,r,a){if(r instanceof Array){var n=r.length,l=0,s=n,c=1;e.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g,function(e,t,r,a){l=parseInt(t||l),s=parseInt(r||s),c=parseInt(a||c)}),l=0>l?Math.max(0,l+n):Math.min(n,l),s=0>s?Math.max(0,s+n):Math.min(n,s);for(var o=l;s>o;o+=c)P.trace(o+";"+t,r,a)}},eval:function(x,_v,_vname){try{return $&&_v&&eval(x.replace(/#/g,"_v"))}catch(e){throw new SyntaxError("jsonPath: "+e.message+": "+x.replace(/#/g,"_v").replace(/\^/g,"_a"))}}},$=obj;return expr&&obj&&("VALUE"==P.resultType||"PATH"==P.resultType)?(P.trace(P.normalize(expr).replace(/^\$;/,""),obj,"$"),P.result.length?P.result:!1):void 0}
// some extensions I have added to JSONPath
var jsonPathStore = function(obj,path,values) {
var maps=jsonPath(obj, path,{resultType:"PATH"})
maps.map(function(item,index) {
return eval( '(' + item.replace(/\$/,"obj") + '="' + values[index] +'"' + ')' );
})
}
var jsonPathDelete = function(obj,path) {
var maps=jsonPath(obj, path,{resultType:"PATH"})
maps.map(function(item,index) {
return eval( '(' + 'delete ' + item.replace(/\$/,"obj") + ')' );
})
}
var jsonPathRead = function(obj,path) {
var maps=jsonPath(obj, path,{resultType:"PATH"})
return maps.map(function(item,index) {
return eval( '(' + item.replace(/\$/,"obj") + ')' );
})
}
var jsonObject = [{
"type": "test",
"id": "100",
"values": {
"name": "Alpha"
},
"validations": []
}, {
"type": "services",
"validations": [{
"id": "200",
"name": "John",
"selection": [{
"id": "300",
"values": {
"name": "Blob"
}
}]
}]
}]
// this XPath will read all the id properties starting from the root element
console.log( "jsonPathRead All Ids" + JSON.stringify(jsonPathRead(jsonObject,"$..id"), null, 2) )
function getIds(obj) {
for (var x in obj) {
if (typeof obj[x] === 'object') {
getIds(obj[x]);
} else if (x === 'id') {
console.log(obj.id);
}
}
}
var City ={
"City1": [{"name": "Place 1"},{"name": "Place 2"},{"name": "Place 3"}],
"City2":[{"name":"My Place 1"},{"name":"My Place 2"},{"name":"My Place 3"}],
"City3":[{"name":"My Place 1"},{"name":"My Place 2"},{"name":"My Place 3"}]
};
I want to use JavaScript to loop to get the first city name, and then loop to get its places, then get the second city and its places and so on.
You could use a for loop to go through all properties of the City object:
for (var key in City) {
if (City.hasOwnProperty(key)) {
var values = City[key];
// "key" will be City1, City2, ...
// "values" will be the corresponding array through which
// you can loop
}
}
function nestedLoop(obj) {
const res = {};
function recurse(obj, current) {
for (const key in obj) {
let value = obj[key];
if(value != undefined) {
if (value && typeof value === 'object') {
recurse(value, key);
} else {
// Do your stuff here to var value
res[key] = value;
}
}
}
}
recurse(obj);
return res;
}
Try utilizing Object.keys() , Array.prototype.forEach()
var City = {
"City1": [{
"name": "Place 1"
}, {
"name": "Place 2"
}, {
"name": "Place 3"
}],
"City2": [{
"name": "My Place 1"
}, {
"name": "My Place 2"
}, {
"name": "My Place 3"
}],
"City3": [{
"name": "My Place 1"
}, {
"name": "My Place 2"
}, {
"name": "My Place 3"
}]
};
Object.keys(City).forEach(function(value, key) {
console.log(value);
City[value].forEach(function(v, k) {
console.log(v.name)
})
});
for (let key in City) {
let value = City[key];
console.log(key);
for (i = 0; i <= value.length; i++) {
console.log(value[i]);
}
}
I was trying to implement a search in a nested object.
// Returns an array of matching objects
function getObjects(obj, key, val) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == 'object') {
objects = objects.concat(getObjects(obj[i], key, val));
} else if (!$.isNumeric(obj[key]) && i == key && obj[key].toLowerCase().match(val)) {
objects.push(obj);
}
}
return objects;
}
This function returns the matching object on basis of key value pair provided.
What I want is the path to the object where key value pair is found.
Sample data
TestObj = {
"Categories": [{
"Product1": [{
"id": "a01",
"name": "Pine",
"description": "Short description of pine."
}, {
"id": "a02",
"name": "Pine",
"description": "Short description of pine."
}, {
"id": "a03",
"name": "Poplar",
"description": "Short description of poplar."
}],
"id": "A",
"title": "Cheap",
"description": "Short description of category A."
}, {
"Product2": [{
"id": "b01",
"name": "Maple",
"description": "Short description of maple."
}, {
"id": "b02",
"name": "Oak",
"description": "Short description of oak."
}, {
"id": "b03",
"name": "Bamboo",
"description": "Short description of bamboo."
}]
}]
};
I was trying to write a function
function objPath(obj, key, val, path) {
var result = [];
var passName = '';
if (path) {
passName = path;
}
var tempArray = [];
for (var prop in obj) {
var value = obj[prop];
if (typeof value === 'object') {
tempArray = objPath(value, key, val, passName);
$.each(tempArray, function (k, value) {
result.push(value);
});
} else if (prop == key && obj[key].toLowerCase().match(val)) {
result.push(obj[key]);
}
}
return result;
}
If I call function as
objPath(TestObj, 'id', 'b03');
Which should return Categories > Product2 > 3rd Row
But all I'm getting is the key. How to fix the objPath function to obtain required result
I have wrote a custom function
function objPath(obj, key, val, path) {
var result = [];
var passName = '';
if (path) {
passName = path;
}
var tempArray = [];
for (var prop in obj) {
var value = obj[prop];
if (typeof value === 'object') {
tempArray = objPath(value, key, val, passName);
$.each(tempArray, function (k, value) {
result.push(value);
});
} else if (!$.isNumeric(obj[key]) && prop == key && obj[key].toLowerCase().match(val)) {
result.push(passName + '["' + obj[prop] + '"]');
} else {
if ($.isNumeric(obj[prop])) {
//passName += ' > ' + obj[prop];
} else {
passName += '["' + obj[prop] + '"]';
}
}
}
return result;
}
Which will return
["Categories"]["Product2"]["b03"]