javascript how to compare only keys and not values of json - javascript

I have two nested JSON objects
//Json object1
{
"version": "1",
"user": {
"id": 123
}
}
//Json object2 =
{
"version": "1",
"user": {
"i": 123
}
}
1)It should only check for keys and not values during comparison.
So above should return false and below should return true
2) It should also follow the structure say user.id is not the same as just id.
//Json object1
{
"version": "1",
"user": {
"id": 123
}
}
//Json object2
{
"version": "1",
"user": {
"id": 12
}
}
I have tried the code below but to no avail
exports.compareObjects = async(model, response) => {
switch (Object.prototype.toString.call(model)) {
case '[object]':
var x;
var mKeys = Object.keys(model);
for (x in mKeys) {
return this.compareObjects(Object.keys(model)[x], Object.keys(response)[x]);
}
break;
case '[object Object]':
var x1;
var mKeys1 = Object.keys(model);
for (x1 in mKeys1) {
return this.compareObjects(Object.keys(model)[x1], Object.keys(response)[x1]);
}
break;
case '[object Array]':
return this.compareObjects(model[0], response[0]);
// case '[object String]':
// return model === response;
default:
return true;
}
};

This uses a recursive key search to build out an array of keys in each object you want to compare.
It tests fairly strict equality (no extra keys in either object)
let obj1 = JSON.parse(`{
"version": "1",
"user": {
"id": 123
}
}`);
let obj2 = JSON.parse(`{
"version": "1",
"user": {
"i": 123
}
}`);
let obj3 = JSON.parse(`{
"version": "1",
"user": {
"id": 123
}
}`);
let obj4 = JSON.parse(`{
"version": "1",
"user": {
"id": 12
}
}`);
let test1 = structureIsEqual(obj1, obj2);
let test2 = structureIsEqual(obj3, obj4);
console.log('Structure of first two match: ' + test1);
console.log('Structure of second two match: ' + test2);
function structureIsEqual(obj1, obj2) {
let tree1 = getKeys(obj1).sort();
let tree2 = getKeys(obj2).sort();
if(tree1.length !== tree2.length)
return false;
let mismatch = tree1.find((x, idx) => tree2[idx] !== x);
return !mismatch;
}
function getKeys(obj) {
return recursiveKeys(obj, [], []);
}
function recursiveKeys(obj, result, todo, root = '') {
Object.keys(obj).forEach(key => {
if(typeof obj[key] === 'object') {
result.push(root + key);
todo.push({ obj: obj[key], root: root + key + '.' });
} else {
result.push(root + key);
}
});
if(todo.length > 0) {
let todoItem = todo.pop();
return recursiveKeys(todoItem.obj, result, todo, todoItem.root);
}else {
return result;
}
}

Could be you could fix it by this line
return this.compareObjects(Object.keys(model)[x1], Object.keys(response)[x1]);
Instead do
return this.compareObjects(model[x1], response[x1]);
I would do it like this
1. you only need to compare objects (all other dont have keys)
2. recurse if value is also object
function compare(obj,model){
let keys=Object.keys(model)
let thisLevelOK=true
for (let key in keys.length){
if (obj[key]===undefined){
thisLevelOK=false
break
} else if (model[key].toString()=="[object Object]" && compare(obj[key],model[key])==false){
thisLevelOK=false
break
}
}
//in case obj has more keys then model
thisLevelOK=thisLevelOK && compare(model,obj)
return thisLevelOK
}

var compareObjects = (model, response) => {
switch (Object.prototype.toString.call(model)) {
case '[object]':
var x;
var mKeys = Object.keys(model);
for (x in mKeys) {
return this.compareObjects(Object.keys(model)[x], Object.keys(response)[x]);
}
break;
case '[object Object]':
var x1;
var mKeys1 = Object.keys(model);
for (x1 in mKeys1) {
let t = this.compareObjects(Object.keys(model)[x1], Object.keys(response)[x1]);
if(!t){
return false
}
if(typeof model[mKeys1[x1]] == "object"){
return this.compareObjects(model[mKeys1[x1]], response[mKeys1[x1]])
}
}
case '[object Array]':
return this.compareObjects(model[0], response[0]);
case '[object String]':
return model === response;
default:
return true;
}
};
let a = {
"version": "1",
"user": {
"id": 123,
"n":"d",
"j":{
"ns":"m"
}
}
}
let b = {
"version": "1",
"user": {
"id": 123,
"n":"d",
"j":{
"ns":"m"
}
}
}
var t = compareObjects(a,b)
console.log(t)

Related

Compare 2 Object and list out Missing key and Change in Value Recursively [duplicate]

This question already has answers here:
How to compare two objects and get key-value pairs of their differences?
(7 answers)
Closed 4 months ago.
want to compare 2 out and miss out missing field and change in value recursively
obj1 = {
"val1": "test",
"stream": {
"key": true,
"value": false
},
"val2": "test",
"val3": "test",
}
obj2 = {
"val1": "test",
"stream": {
"key": false,
"value": false
}
}
I need 2 outputs as below
output1 = {
"val2": "test",
"val3": "test"
}
output2 = {
"stream": {
"key": true
}
}
It is some version of deep comparing objects. I combined both outputs into one function since they both represent a different attribute.
var obj1 = {
"val1": "test",
"stream": {
"key": true,
"value": false
},
"val2": "test",
"val3": "test",
}
var obj2 = {
"val1": "test",
"stream": {
"key": false,
"value": false
}
}
function diffCompare(obj1, obj2) {
var list = [];
function is_obj(obj) {
return typeof obj === 'object' && obj !== null
}
function iterate(obj1, obj2, path) {
path = path || []
Object.keys(obj1).forEach(function(key) {
if (obj1[key] != obj2[key]) {
if (is_obj(obj1[key]) && is_obj(obj2[key])) {
iterate(obj1[key], obj2[key], path.concat(key))
} else {
list.push({
path: path.concat(key),
value: obj1[key]
})
}
}
})
}
iterate(obj1, obj2)
// console.log(list)
// building result object:
var result = list.reduce(function(agg, {path, value}) {
var pointer = agg;
while (path.length) {
var part = path.shift();
pointer[part] = pointer[part] || {}
if (path.length) {
pointer = pointer[part]
} else {
pointer[part] = value;
}
}
return agg;
}, {});
return result;
}
console.log(diffCompare(obj1, obj2))
.as-console-wrapper {
max-height: 100% !important
}

Convert keys from key value pair to capital case using javaScript

I am trying to convert the keys in JSON to the capital case using Javascript. I am successful to some extent. However, it is not creating the arrays in the correct way. It is inserting numbers before every object inside an array.
Input:
{
"id": "123",
"retweetCheck": {
"result": "OK",
"checks": [
{
"cId": "123"
},
{
"cId": "456"
}
]
},
"tweetCheck": {
"result": "OK",
"cId": "345",
"check": "Fail"
}
}
Code to convert the keys to capital case:
var responseContent = context.getVariable("response.content") || "";
responseContent = JSON.parse(responseContent) || "";
transformedCapitalizedObj = keysToCapitalCase(responseContent);
var finalResponseObj = {
Data: transformedCapitalizedObj
};
context.setVariable("response.content", JSON.stringify(finalResponseObj));
The function
function objEntries(obj) {
const keys = Object.keys(obj);
const keyValuePairs = keys.map(key => {
const value = obj[key];
return [key, value];
});
return keyValuePairs;
}
function keysToCapitalCase(objToProcess) {
if (!objToProcess || typeof objToProcess !== "object") return null;
var finalObj = {};
objToProcess = objEntries(objToProcess);
objToProcess.forEach(function (entry) {
var key = entry[0];
var value = entry[1];
key = key.charAt(0).toUpperCase() + key.slice(1);
if (typeof value == "object" || (value instanceof Array)) {
value = keysToCapitalCase(value);
}
finalObj[key] = value;
});
return finalObj;
}
The output I am getting currently is:
{
"Data":{
"RetweetCheck":{
"Checks":{
"0":{
"CId":"123"
},
"1":{
"CId":"456"
}
},
"Result":"OK"
},
"Id":"123",
"TweetCheck":{
"CId":"345",
"Check":"Fail",
"Result":"OK"
}
}
}
But ideally, the output should look like this:
{
"Data": {
"Id": "123",
"RetweetCheck": {
"Result": "OK",
"Checks": [
{
"CId": "123"
},
{
"CId": "456"
}
]
},
"TweetCheck": {
"Result": "OK",
"CId": "345",
"Check": "Fail"
}
}
}
It is basically inserting a serial number before each object inside an array instead of []. How this can be rectified. Any help will really do wonders.
When you call your function keysToCapitalCase(), first check if you have an array (with ES6, you can do this using Array.isArray()), and if you do, you can map the objects / inner arrays within that array to the result of recursively calling your keysToCapitalize function. Otherwise, if you get a standard object that isn't an array, you can perform your standard object mapping:
const obj = { "id": "123", "retweetCheck": { "result": "OK", "checks": [{ "cId": "123" }, { "cId": "456" } ] }, "tweetCheck": { "result": "OK", "cId": "345", "check": "Fail" } };
function keysToCapitalCase(objToProcess) {
if (!objToProcess || typeof objToProcess !== "object") return null;
if(Array.isArray(objToProcess)) {
return objToProcess.map(obj => keysToCapitalCase(obj));
}
var finalObj = {};
objToProcess = Object.entries(objToProcess); // if you can support it, `Object.entries()` does what `objEntries` does
objToProcess.forEach(function(entry) {
var key = entry[0];
var value = entry[1];
key = key.charAt(0).toUpperCase() + key.slice(1);
if (typeof value == "object") {
value = keysToCapitalCase(value);
}
finalObj[key] = value;
});
return finalObj;
}
var finalResponseObj = {Data: keysToCapitalCase(obj)};
console.log(finalResponseObj);
I would probably write the above method in a similar way, but instead using some inbuilt functions to make it a little more concise, such as .map() and Object.fromEntries():
const obj = { "id": "123", "retweetCheck": { "result": "OK", "checks": [{ "cId": "123" }, { "cId": "456" } ] }, "tweetCheck": { "result": "OK", "cId": "345", "check": "Fail" } };
const cap = str => str.charAt(0).toUpperCase() + str.slice(1);
const keysToCapitalCase = (objToProcess) => {
if (Object(objToProcess) !== objToProcess) return null;
return Array.isArray(objToProcess)
? objToProcess.map(obj => keysToCapitalCase(obj))
: Object.fromEntries(Object.entries(objToProcess).map(([key, val]) => [
cap(key), Object(val) === val ? keysToCapitalCase(val) : val
]));
}
const finalResponseObj = {Data: keysToCapitalCase(obj)};
console.log(finalResponseObj);
As indicated by the {} brackets, instead of the wanted [] brackets you are creating an empty object and not an Array.
To create an Array just change your var finalObj = {}; to var finalObj = [];
with an Array finalObj[key] = value will no longer work, you will now have to use finalObj.push(value)

Fill a JSON from another using javascript

Given an empty JSON 1:
JSON1 = {
"person": { "firstName": "" },
"products": { "packName": "", "packSize": "" }
}
and JSON 2 which has more fields than JSON 1:
JSON2 = {
"person": { "firstName": "Ahmed", "job": "Doctor" },
"products": { "packName": "antibiotic", "packSize": "large" }
}
I want to fill JSON 1 with its corresponding values in JSON 2
{
"person": { "firstName": "Ahmed" },
"products": { "packName": "antibiotic", "packSize": "large" }
}
I have tried several methods but not getting there
var newObj = {};
var parsedJson1 = JSON.parse(tw.local.JSON1);
var parsedJson2 = JSON.parse(tw.local.JSON2);
var i;
for (i in parsedJson1) {
var key=i;
var subkey=i;
for (j in parsedJson2) {
var k=j;
var s=j;
if (key == k && subkey == s) {
newObj[key][subkey] = parsedJson2[j];
}
}
}
tw.local.stringifiedJSON = JSON.stringify(newObj);
Here's an example on how to do it:
Note that this assumes the following:
you know the second object will have all required keys.
all values at the first level are objects
note that this solution will work only for the specified structure, and if you need to handle any variations you will need to make it more defensive.
Steps:
iterate over the valid keys (all keys on JSON1)
for each valid key, add it to newObj and iterate over valid subKeys
copy values from JSON2 to newObj
Note: I changed your code a bit so it can be executed on this site
const JSON1 = `{
"person": { "firstName": "" },
"products": { "packName": "", "packSize": "" }
}`;
const JSON2 = `{
"person": { "firstName": "Ahmed", "job": "Doctor" },
"products": { "packName": "antibiotic", "packSize": "large" }
}`;
const parsedJson1 = JSON.parse(JSON1);
const parsedJson2 = JSON.parse(JSON2);
const newObj = {};
const validKeys = Object.keys(parsedJson1);
for (let i of Object.keys(parsedJson1)) {
if (newObj[i] === undefined) {
newObj[i] = {};
}
for (let j of Object.keys(parsedJson1[i])) {
newObj[i][j] = parsedJson2[i][j];
}
}
console.log(JSON.stringify(newObj));
You should use recursion to traverse target object for robustness. I formatted code for better readability.
function getNode(obj, path) {
var ret = obj;
try {
// replace forEach with for-loop for browser compatibilities
path.forEach(function (p) {
ret = ret[p];
});
} catch (e) {
return undefined;
}
return ret;
}
function fillJSONWithAnother(target, source, path) {
if (!(target && source)) return target;
// Assign path as an empty array for first call
path = path || [];
// Get target node and source node for comparing
var targetValue = getNode(target, path);
var sourceValue = getNode(source, path);
// targetValue is not a leaf node (i.e. is Object), should traverse its children nodes
if (targetValue && typeof targetValue === "object") {
for (var key in targetValue) {
fillJSONWithAnother(target, source, path.concat([key]));
}
}
// targetValue is a leaf node (i.e. not Object) and source value exists, set corresponding value
else if (sourceValue !== undefined) {
var node = getNode(target, path.slice(0, path.length - 1));
node[path[path.length - 1]] = sourceValue;
}
return target;
}
// Test case
var obj1 = {
"person": {
"firstName": ""
},
"products": {
"packName": "",
"packSize": "l"
}
};
var obj2 = {
"person": {
"firstName": "Ahmed",
"job": "Doctor"
},
"products": {
"packName": "antibiotic",
"packSize": "large"
}
}
fillJSONWithAnother(obj1, obj2);
// {"person":{"firstName":"Ahmed"},"products":{"packName":"antibiotic","packSize":"large"}}

Generic solution to create an Object of unknown deepth from an Array

I need to create a JSON object as follows:
{
"app_name": "foo",
"meta": {
"title": "foo",
"lang": "bar"
},
"teaser": {
"foo": {
"headline": "foo",
"subline": "bar"
}
}
}
The object tree is provided in the following representation:
var translations = [
{
"key": "app_name",
"val": "foo"
},
{
"key": "meta.title",
"val": "foo"
},
{
"key": "meta.lang",
"val": "bar"
},
{
"key": "teaser.foo.headline",
"val": "foo"
},
{
"key": "teaser.foo.subline",
"val": "bar"
}
];
Now, I can't find a generic solution at the moment. Here's some code that would work for the given (simplified) example:
var finalObject = {};
for (var i in translations) {
var translation = translations[i],
translationKey = translation["key"],
translationVal = translation["val"];
var keyArray = translationKey.split("."),
keyDepth = keyArray.length;
if (keyDepth === 1) {
finalObject[keyArray[0]] = translationVal;
} else if (keyDepth === 2) {
if (typeof finalObject[keyArray[0]] === 'object') {
finalObject[keyArray[0]][keyArray[1]] = translationVal;
} else {
var item = {};
item[keyArray[1]] = translationVal;
finalObject[keyArray[0]] = item;
}
} else if (keyDepth === 3) {
if (finalObject[keyArray[0]] && finalObject[keyArray[0]][keyArray[1]] && typeof finalObject[keyArray[0]][keyArray[1]] === 'object') {
finalObject[keyArray[0]][keyArray[1]][keyArray[2]] = translationVal;
} else {
var item = {};
item[keyArray[2]] = translationVal;
if (!finalObject[keyArray[0]] || typeof finalObject[keyArray[0]] !== 'object') {
finalObject[keyArray[0]] = {};
}
finalObject[keyArray[0]][keyArray[1]] = item;
}
}
}
But this is as ugly as unreliable. I'd like to replace this part:
if (keyDepth === 1) {
finalObject[keyArray[0]] = translationVal;
} else if (keyDepth === 2) {
//...
}
with
if (keyDepth === 1) {
finalObject[keyArray[0]] = translationVal;
} else {
//some generic solution, which works for any deepth
}
Any ideas?
You could take an iterative approach by using a default object if a property does not exists.
function setValue(object, key, value) {
var path = key.split('.'),
last = path.pop();
path.reduce(function (o, k) {
return o[k] = o[k] || {};
}, object)[last] = value;
}
var translations = [{ key: "app_name", val: "foo" }, { key: "meta.title", val: "foo" }, { key: "meta.lang", val: "bar" }, { key: "teaser.foo.headline", val: "foo" }, { key: "teaser.foo.subline", val: "bar" }],
object = {};
translations.forEach(function (o) {
setValue(object, o.key, o.val);
});
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Collapse nested objects into flat map with dotted properties

I have an object like this
{
"CPU": {
"Architecture": {
"X86": 0,
"Other": 0,
"X86_64": 6
}
},
"Platform": {
"Os": {
"Mac": 0,
"Linux": 5,
"Other": 0,
"Windows": 0
}
}
}
How to to convert it to map like this?
"CPU.Architecture.X86": 0
"Platfrom.Os.Mac":0
"Platfrom.Os.Linux":5
Can there already be ready-made solutions?
If your goal is to concatenate nested object property keys, there is no built-in function for that. However, it is quite simple to implement your own solution:
// Concatenate nested object property keys with a dot:
function dotnotation(obj, dst = {}, prefix = '') {
Object.entries(obj).forEach(([key, val]) => {
if (val && typeof val == 'object') dotnotation(val, dst, prefix + key + '.');
else dst[prefix + key] = val;
});
return dst;
}
// Example:
console.log(dotnotation({a:{b:1, c:2}, d:3})); // {'a.b':1, 'a.c':2, 'd':3}
Object.entries() is part of the upcoming ES2017 standard.
You could try something like this, assuming your object are simply created from data:
function flatten(obj, prev = "", res = {}){
for(f in obj){
if (obj.hasOwnProperty(f)){
if(obj[f].constructor === Object){
prev = prev + f + ".";
flatten(obj[f], prev, res);
}else{
res[prev + f] = obj[f];
}
}
}
return res;
}
let a = {
"CPU": {
"Architecture": {
"X86": 0,
"Other": 0,
"X86_64": 6
}
},
"Platform": {
"Os": {
"Mac": 0,
"Linux": 5,
"Other": 0,
"Windows": 0
}
}
};
console.log(flatten(a))
It will use the for... in loop.
// more consisely
function flatten(obj, prev = "", res = {}){
for(f in obj){
if (obj.hasOwnProperty(f)){
if (obj[f].constructor === Object){
if(typeof obj[f] !== "object") res[prev + f] = obj[f];
else flatten(obj[f], prev + f + ".", res);
}
}
}
return res;
}

Categories

Resources