Remove all the keys from nested object excluding one key in javascript - javascript

I have a JSON object as follows:
x = {
"prop1": {
"description": "prop1",
"dataType": "string",
"value" : "abc"
},
"prop2": {
"sub1": {
"description": "sub1",
"dataType": "integer",
"value" : 12
},
"sub2": {
"description": "sub2",
"dataType": "integer"
}
},
"prop3": {
"input": {
"name": {
"description": "input messages",
"dataType": "boolean",
"value": false
}
},
"output": {
"description": "output messages",
"dataType": "boolean",
"value": false
}
}
}
In the above object, I wanted to remove the keys(description, dataType), also remove the key if it doesn't have any value key. the expected output for the above one is as below.
y = {
"prop1": {
"value" : "abc"
},
"prop2": {
"sub1": {
"value" : 12
}
},
"prop3": {
"input": {
"name": {
"value": false
}
},
"output": {
"value": false
}
}
}
My current solution is as follows:
function findValue(obj, string, obj1) {
if (obj.hasOwnProperty("value")) {
obj1[string.substring(1)] = obj.value
return
}
for (var key in obj) {
findValue(obj[key], [string, key].join("."), obj1)
}
}
console.log(x);
var x1 = {}
findValue(x, "", x1)
var y = {};
function assign(obj, keyPath, value) {
const lastKeyIndex = keyPath.length - 1;
for (var i = 0; i < lastKeyIndex; ++i) {
const key = keyPath[i];
if (!(key in obj)) {
obj[key] = {}
}
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = { "value": value };
}
Object.keys(x1).forEach(key => {
const keyPath = key.split('.');
let value = x1[key];
if (value != null) {
this.assign(y, keyPath, value);
}
});
console.log(y);
I did it in a very long way. I first convert my object into a format where each key is the combination of parent and child keys and then converted it into the expected format.
Is there any way to make it better?

Related

How to map JSON object's values, with another object's values when that object is having key similar to the JSON object value

I have a JSON object that I want to change values dynamically with the object that comes from front end(ReactJs) which has same value as the key of that object.here is my JSON object.
var jsonObj = {
"fields": {
"field1": {
"key": "value_1"
},"field2": {
"name": "value_3"
},
"field3":{"accountId":"value_9"},
"field4": "value_2",
"field5": "value_4",
"field6": "value_5",
"field7": {
"value": "value_6"
},
"field8": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{
"text": "value_7",
"type": "text"
}
]
}
]
}
}
}
This is my other object that comes from React front-end
Obj1 {
value_4: '896',
value_9: '62cbef88ec233f24600c',
value_5: 'Software',
value_7: 'description from front',
value_6: 'test#gmail.com',
value_1: 'TRDSD',
value_2: 'Test sum',
value_8: '',
value_10: '',
value_11: '',
value_12: '',
value_13: '',
value_3: 'TRDSD_incident'
}
What I want is this,
{
"fields": {
"field1": {
"key": "TRDSD"
},"field2": {
"name": "TRDSD_incident"
},
"field3":{"accountId":"62cbef88ec233f24600c"},
"field4": "Test sum",
"field5": "896",
"field6": "Software",
"field7": {
"value": "test#gmail.com"
},
"field8": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{
"text": "description from front",
"type": "text"
}
]
}
]
}
}
}
Already I achieve this using this function.here what I do is first I take keys that have no nested objects into an array and check for each field.also I have same function for nested object keys and check for each in the same way( value,key,name,accountId). and finally I get the JSON object as I expected.
function usingTree2(obj2, map) {
function traverse2(children) {
for (let item of children) {
if (item && item.field4) {
item.field4 = map[item.field4];
}
if (item && item.field5) {
item.field5 = map[item.field5];
}
if (item && item.field6) {
item.field6 = map[item.field6];
}
if (item && item.field8.content[0].content[0].text) {
item.field8.content[0].content[0].text =
map[item.field8.content[0].content[0].text];
}
if (item && item.children) {
traverse2(item.children);
}
}
}
traverse2(obj2);
return obj2;
}
function usingTree(obj, map) {
function traverse(children) {
for (let item of children) {
if (item && item.key) {
item.key = map[item.key];
} else if (item && item.value) {
item.value = map[item.value];
} else if (item && item.name) {
item.name = map[item.name];
} else if (item && item.accountId) {
item.accountId = map[item.accountId];
} else if (item && item.id) {
item.id = map[item.id];
}
if (item && item.children) {
traverse(item.children);
}
}
}
traverse(obj);
return obj;
}
const map = obj1
var obj = Object.values(jsonObj.fields);
var obj2 = Object.values(jsonObj);
usingTree(obj, map);
usingTree2(obj2, map);
for (let i = 0; i < fieldCount; i ++) {
usingTree(obj, map);
}
for (let i = 0; i < fieldCount; i ++) {
usingTree2(obj2, map);
}
But the problem is fields in JSON object are changing. So instead of checking for each field how can I achieve this dynamically.Of course without using same function twice for nested objects. I know this is not a best way. I am new to Nodejs, so please help me to do this. Hope my question is clear. Thanks in advance.

Get values for a matching key recursively from object

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));

Compare 2 nested JSON and highlight the differences them Javascript

I am working on a requirement where I need to compare 2 JSON Objects. Comparing JSON 1 and JSON 2 should be such that the Result JSON should be JSON1 keys and the values should be fetched from JSON2. The values should only be changed for same keys with different values. Also, I need to highlight only the changed values in the result. Here is my code where I have been able to compare and get the JSON result, I am only stuck with highlighting the changed values. May I know where did I go wrong or what is missing?
var compareJSON = function(obj1, obj2) {
var ret = {};
for(var i in obj2) {
if(!obj1.hasOwnProperty(i) || obj2[i] !== obj1[i]) {
ret[i] = obj2[i];
}
}
return ret;
};
//JSON 1
var a = {
"name": [
"complex"
],
"dfts": [
{
"valuec": {
"valuesid": "1232"
},
"ids": {
"idp": "chanellp"
}
}
],
"container": {
"contid": "na",
"dpvalueus": {
"ftsme": "na"
},
"attributes": {
"channelpo": "na"
},
"item": [
{
"contid": {
"stjsk": "wher"
},
"quantity": "na",
"dpvalue": {
"valuers": "na"
}
}
]
}
};
//JSON 2
var b = {
"name": [
"simple"
],
"dfts": [
{
"valuec": {
"valuesid": "75756754"
},
"ids": {
"idp": "where"
}
}
],
"container": {
"contid": "360",
"dpvalueus": {
"ftsme": "100"
},
"attributes": {
"channelpo": "usual"
},
"item": [
{
"contid": {
"stjsk": "stkh"
},
"quantity": "1",
"dpvalue": {
"valuers": "wholesome"
}
}
]
}
};
console.log(compareJSON(a, b));
Result can be seen in console.
I would add the functionality that your are missing ( highlight the differences) here:
Change
if(!obj1.hasOwnProperty(i) || obj2[i] !== obj1[i]) {
ret[i] = obj2[i];
}
To:
if(!obj1.hasOwnProperty(i)) {
ret[i] = obj2[i];
continue;
}
if( obj2[i] !== obj1[i] )
{
obj2[i]->diffent = true;
ret[i] = obj2[i];
continue;
}
ret[i] = obj2[i];

Find multiple ocurences of a specific value in JSON

I have a complex JSON like this:
{
"a": {
"b": {
"c": {
"price": {
"type": "coin",
"value": "currency"
}
}
}
},
"e": {
"f": {
"price": {
"type": "note",
"value": "currency"
}
}
},
"price": {
"type": "cents",
"value": "dollars"
}
}
I am trying to write a JavaScript function that will find "price" at any location and pull out the "type" and "value" for it. So my output should be:
"coin" : "currency"
"note" : "currency"
"cents" : "dollars"
You can create recursive function with for...in loop to return object as a result.
const data = {"a":{"b":{"c":{"price":{"type":"coin","value":"currency"}}}},"e":{"f":{"price":{"type":"note","value":"currency"}}},"price":{"type":"cents","value":"dollars"}}
function getPrice(data) {
const result = {}
for (let i in data) {
if (typeof data[i] == 'object') Object.assign(result, getPrice(data[i]))
if (i == "price") Object.assign(result, {
[data[i].type]: data[i].value
})
}
return result;
}
const result = getPrice(data);
console.log(result)
You could check the wanted key price and take type and value for a new object, or look deeper.
function flat(object) {
return Object.entries(object).reduce(
(r, [k, v]) => Object.assign(r, k === 'price' ? { [v.type]: v.value } : flat(v)),
{}
);
}
var object = { a: { b: { c: { price: { type: "coin", value: "currency" } } } }, e: { f: { price: { type: "note", value: "currency" } } }, price: { type: "cents", value: "dollars" } };
console.log(flat(object));
You need to recursively (or not) iterate on the properties of your object.
Here is some old-school javascript:
const testObj = {
"a":{
"b" : {
"c" :{
"price" : {
"type" : "coin",
"value" : "currency"
}
}
}
},
"e" : {
"f" : {
"price" : {
"type" : "note",
"value": "currency"
}
}
},
"price": {
"type": "cents",
"value": "dollars"
}
};
function findOccurences(accum, obj, prop) {
if(obj.hasOwnProperty(prop)) {
accum.push(obj[prop]);
}
for(var p in obj) {
if(obj.hasOwnProperty(p) && p !== prop)
findOccurences(accum, obj[p], prop);
}
}
var accum = [];
findOccurences(accum, testObj, "price");
console.log(accum);
While the other answers are good, they don't allow different values for the same key. For example, if you have an additional price like this:
"g": {
"price": {
"type": "coin",
"value": "dollars"
}
}
It will overwrite the first value of coin with the other answers and you will end up with:
{
"coin": "dollars",
"note": "currency",
"cents": "dollars"
}
If you have that scenario and want to get the two different values of coin, you'll need to use a separate object for each key/value instead of making them properties of a single object:
var json = {
"a": {
"b": {
"c": {
"price": {
"type": "coin",
"value": "currency"
}
}
}
},
"e": {
"f": {
"price": {
"type": "note",
"value": "currency"
}
}
},
"price": {
"type": "cents",
"value": "dollars"
},
"g": {
"price": {
"type": "coin",
"value": "dollars"
}
}
};
function getPrice(data) {
var result = [];
for (let i in data) {
if (i == "price")
result.push({
[data[i].type]: data[i].value
});
else if (typeof data[i] == "object")
result.push(getPrice(data[i])[0]);
}
return result;
}
var price = getPrice(json);
console.log(price)

How to get the key value from nested object

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);
}
}
}

Categories

Resources