Collapse nested objects into flat map with dotted properties - javascript

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

Related

Serialize complex object with nested array and objects to build query params

Example of complex object which i want to serialize, so I can use it in url as query param
var query = {
"page": {
"number": 1,
"size": 50
},
"sort": "engagement_total",
"sort_direction": "asc",
"filters": {
"engagement_total": {
"type": "range",
"value": [
21,
46
]
}
}
}
JSON.stringify(query)
result with stringify
'{"page":{"number":1,"size":50},"sort":"engagement_total","sort_direction":"asc","filters":{"engagement_total":{"type":"range","value":[21,46]}}}'
Desired result
filters%5Bengagement_total%5D%5Btype%5D=range&filters%5Bengagement_total%5D%5Bvalue%5D%5B%5D=21&filters%5Bengagement_total%5D%5Bvalue%5D%5B%5D=46&page%5Bnumber%5D=1&page%5Bsize%5D=50&sort=engagement_total&sort_direction=asc
let obj = {
"page": {
"number": 1,
"size": 50
},
"sort": "engagement_total",
"sort_direction": "asc",
"filters": {
"engagement_total": {
"type": "range",
"value": [
21,
46
]
}
}
}
var flattenObj = function(data) {
var result = {};
function recurse(cur, prop) {
if (Object(cur) !== cur) {
result[prop] = cur;
} else if (Array.isArray(cur)) {
for (var i = 0, l = cur.length; i < l; i++)
recurse(cur[i], prop + "[" + i + "]");
if (l == 0)
result[prop] = [];
} else {
var isEmpty = true;
for (var p in cur) {
isEmpty = false;
recurse(cur[p], prop ? prop + "[" + p + "]" : p);
}
if (isEmpty && prop)
result[prop] = {};
}
}
recurse(data, "");
return result;
};
let output = flattenObj(obj);
let url = Object.entries(output).map(elem => (elem[0].replace(/\[[0-9]+\]/, '[]') + "=" + elem[1])).join("&")
console.log("url:", url)
console.log("encodeURI:", encodeURI(url))

javascript how to compare only keys and not values of json

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)

Javascript - Traverse through the Json Object and get each item hierarchical key including nested objects and arrays

I want to get the values and keys including these as to any JSON objects as a generic method to use even for the complex objects
json
{
"timezone": 5.5,
"schedule": {
"type": "daily",
"options": {
"hour": 10,
"minute": 29
}
}
want the values and keys in hierarchical schema just like these
timezone - 5.5
schedule.type - daily
schedule.type.options.hour - 10
schedule.type.options.minute - 29
Also, I used this function can get the JSON objects all objects keys and values even in the nested arrays and objects in that
function iterate(obj) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property]);
} else {
console.log(property , obj[property])
}
}
}
return obj;
}
PS - Also I
want to use this for arrays also
"dirs": [ { "watchDir": "Desktop/logs", "compressAfterDays": 50 }, { "watchDir": "Desktop/alerts", "timeMatchRegex": "(.*)(\\d{4})-(\\d{2})-(\\d{2})-(\\d{2})_(\\d{2})(.*)", }]
the output I want to be just like this
dirs[0].watchdir="Desktop/alerts"
dirs[1].watchDir="Desktop/logs"
const obj = { "timezone": 5.5, "dirs": [ { "watchDir": "Desktop/logs", "compressAfterDays": 50 }, { "watchDir": "Desktop/alerts", "timeMatchRegex": "(.*)(\\d{4})-(\\d{2})-(\\d{2})-(\\d{2})_(\\d{2})(.*)", }] ,"schedule": { "type": "daily", "options": { "hour": 10, "minute": 29 }, 'available': true } };
function iterate(obj, str) {
let prev = '';
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
const s = isArray(obj) ? prev + str + '[' + property + ']' + '.' : prev + property + (isArray(obj[property]) ? '' : '.');
iterate(obj[property], s);
} else {
prev = (str != undefined ? str : '');
console.log(prev + property, '- ' + obj[property]);
}
}
}
return obj;
}
function isArray(o) {
return o instanceof Array;
}
iterate(obj);
Pass on the keys in the recursive call:
function iterate(obj, path = []) {
for (let property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property], [...path, property]);
} else {
console.log(path, property , obj[property])
}
}
}
}

How can I filter a JSON object in JavaScript?

I've got the following JSON string:
{
"Alarm":{
"Hello":48,
"World":3,
"Orange":1
},
"Rapid":{
"Total":746084,
"Fake":20970,
"Cancel":9985,
"Word": 2343
},
"Flow":{
"Support":746084,
"About":0,
"Learn":0
}
}
Then I load the above string and convert it to json object:
jsonStr = '{"Alarm":{"Hello":48,"World":3,"Orange":1},"Rapid":{"Total":746084,"Fake":20970,"Cancel":9985},"Flow":{"Support":746084,"About":0,"Learn":0}}';
var jsonObj = JSON.parse(jsonStr);
Now, how can I filter this json object by key name?
E.g., if the filter was "ange", the filtered object would be:
{
"Alarm":{
"Orange":1
}
}
If the filter was "flo", the filtered object would become:
{
"Flow":{
"Support":746084,
"About":0,
"Learn":0
}
}
And if the filter was "wor", the result would be:
{
"Alarm":{
"World": 3,
},
"Rapid":{
"Word": 2343
}
}
Is it possible to achieve this filtering using the filter method?
Beside the given solutions, you could use a recursive style to check the keys.
This proposal gives the opportunity to have more nested objects inside and get only the filtered parts.
function filterBy(val) {
function iter(o, r) {
return Object.keys(o).reduce(function (b, k) {
var temp = {};
if (k.toLowerCase().indexOf(val.toLowerCase()) !== -1) {
r[k] = o[k];
return true;
}
if (o[k] !== null && typeof o[k] === 'object' && iter(o[k], temp)) {
r[k] = temp;
return true;
}
return b;
}, false);
}
var result = {};
iter(obj, result);
return result;
}
var obj = { Alarm: { Hello: 48, "World": 3, Orange: 1 }, Rapid: { Total: 746084, Fake: 20970, Cancel: 9985, Word: 2343 }, Flow: { Support: 746084, About: 0, Learn: 0 }, test: { test1: { test2: { world: 42 } } } };
console.log(filterBy('ange'));
console.log(filterBy('flo'));
console.log(filterBy('wor'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can create a function using reduce() and Object.keys() that will check key names with indexOf() and return the desired result.
var obj = {
"Alarm": {
"Hello": 48,
"World": 3,
"Orange": 1
},
"Rapid": {
"Total": 746084,
"Fake": 20970,
"Cancel": 9985,
"Word": 2343
},
"Flow": {
"Support": 746084,
"About": 0,
"Learn": 0
}
}
function filterBy(val) {
var result = Object.keys(obj).reduce(function(r, e) {
if (e.toLowerCase().indexOf(val) != -1) {
r[e] = obj[e];
} else {
Object.keys(obj[e]).forEach(function(k) {
if (k.toLowerCase().indexOf(val) != -1) {
var object = {}
object[k] = obj[e][k];
r[e] = object;
}
})
}
return r;
}, {})
return result;
}
console.log(filterBy('ange'))
console.log(filterBy('flo'))
console.log(filterBy('wor'))
With the filter method I think you mean the Array#filter function. This doesn't work for objects.
Anyway, a solution for your input data could look like this:
function filterObjects(objects, filter) {
filter = filter.toLowerCase();
var filtered = {};
var keys = Object.keys(objects);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (objects.hasOwnProperty(key) === true) {
var object = objects[key];
var objectAsString = JSON.stringify(object).toLowerCase();
if (key.toLowerCase().indexOf(filter) > -1 || objectAsString.indexOf(filter) > -1) {
filtered[key] = object;
}
}
}
return filtered;
}

Iterate and search values in a object-literal having multiple levels

Here is what I want to do:
I have a tree (javascript object-literal structure) with multiple levels.
I have a value of a particular key of this object.
I want to search for this exact key-value pair in the structure and return the value of another key as an output.
For clarity following is my object literal:
{
"nodeId": 1081,
"appId": 150,
"displayText": "Welcome here",
"Nodes": [
{
"nodeId": 2000,
"appId": 150,
"displayText": "Buy",
"parentNodeId": 1081,
"Nodes": [
{
"nodeId": 2003,
"appId": 150,
"displayText": "tCars",
"parentNodeId": 2000,
"Nodes": [
{
"nodeId": 2006,
"appId": 150,
"displayText": "Diesel",
"parentNodeId": 2003,
"Nodes": [
{
"nodeId": 2008,
"appId": 150,
"displayText": "Price", //This is what I want as return value.
"parentNodeId": 2006,
"Nodes": [],
"nodeCode": "RN_1_1_2_1_3_2_4_1",
"parentCode": "RN_1_1_2_1_3_2",
"jumpToNode": "RN_1_1" //here is the value that I have with me.
}
],
"nodeCode": "RN_1_1_2_1_3_2",
"parentCode": "RN_1_1_2_1"
}
],
"concatWithHeader": false,
"nodeCode": "RN_1_1_2_1",
"parentCode": "RN_1_1"
}
],
"nodeCode": "RN_1_1",
"parentCode": "RN"
}
],
"nodeCode": "RN",
"parentCode": "ROOT_NODE"
}
2. Value that I have with me is "RN_1_1" against jumpToNode
3. I want to search in this object literal and get the value of the key displayText
I searched and tried few things for this but couldnt get the logic to iterate over the inner Nodes objects.
Method I wrote so far:
function getObjects(tree){
var searchkey="RN_1_1";
var displayText = "displayText";
var nodeCode = "nodeCode";
var returnText;
if (tree.hasOwnProperty(nodeCode)) {
var obj = tree[nodeCode];
if(obj == searchkey){
returnText = tree[displayText]; //gives me the return text
break;
}
else{
//here I should iterate over the inner `Nodes` and get the required value.
}
}
}
Please help.
Thanks.
I think you can do something like this which works recursively:
function findProperty(obj, prop, val, propToFetch) {
var answer;
if (obj.hasOwnProperty(prop) && obj[prop] === val) {
return obj[propToFetch];
}
for (var i = 0, len = obj.Nodes.length; i < len; i++) {
answer = findProperty(obj.Nodes[i], prop, val, propToFetch);
if (answer !== null) {return answer;}
}
return null;
}
var result = findProperty(data, "jumpToNode", "RN_1_1", "displayText");
Working demo here: http://jsfiddle.net/jfriend00/EjC5V/
Accordingly to your JSON object you can use this way:
var searchKey="RN_1_1",
displayText = "displayText",
nodeCode = "nodeCode",
returnText,
treeSearch = function (obj, searchKey) {
if (obj[nodeCode] === searchKey) {
returnText = obj[displayText];
} else {
if (obj['Nodes'][0]) {
treeSearch(obj['Nodes'][0], searchKey);
} else {
returnText = null
}
}
};
treeSearch(JSONdata, 'RN_1_1_2_1_3_2');
I have flattened the array using the nodeId to be easier to search through it.
After you flatten the array you can filter it as you wish(i suggest using underscorejs.org)
Here is the live example. The result is displayed in the console.
function flattenNodes (obj, newArr) {
if (obj && obj.Nodes) {
var nodes = obj.Nodes;
delete(obj.Nodes);
newArr[obj.nodeId] = obj;
return flattenNodes(nodes.pop(), newArr);
} else {
return newArr;
}
};
var flattenArr = flattenNodes(arr, new Array());
function findInJumpToNode(find) {
for(key in flattenArr) {
if (flattenArr[key] && flattenArr[key]['jumpToNode']) {
if (flattenArr[key]['jumpToNode'] == find) {
return flattenArr[key];
}
}
}
}
var found = findInJumpToNode('RN_1_1');
console.log(found);
You can use recursion to handle your case.
Check out this sample on jsFiddle.
var nodes= [getNodes()];
alert(getObjects(nodes));
function getObjects(tree){
var searchkey="RN_1_1_2_1_3_2_4_1";
var displayText = "displayText";
var nodeCode = "nodeCode";
var returnText;
if(tree.length > 0)
{
if(tree[0]["nodeCode"] === searchkey)
{
return tree[0][displayText];
}
if(typeof tree[0]["Nodes"] === "undefined")
{
return;
}
return getObjects(tree[0]["Nodes"]);
}
}

Categories

Resources