Create a JSON reading recursively from another unknown JSON with Javascript - javascript

I'd like to read recursively all nodes from a generic JSON (so I'm not asking for a script that handles exclusively this kind of structure) and while reading I would to create another JSON in a structure like this (the example is just an excerpt):
[
{
"name":"AgreementOffer",
"size":20000000,
"children":[
{
"name":"Name",
"size":2000000,
"children":[
{
"name":"1cap",
"size":90
}
]
},
{
"name":"Context",
"size":2000000,
"children":[
{
"name":"AgreementInitiator",
"size":200,
"children":[
{
"name":"$SPECS-CUSTOMER",
"size":100
}
]
},
{
"name":"AgreementInitiator",
"size":200,
"children":[
{
"name":"$SPECS-CUSTOMER",
"size":100
}
]
},
{
"name":"AgreementResponder",
"size":200,
"children":[
{
"name":"$SPECS-APPLICATION",
"size":100
}
]
},
{
"name":"AgreementResponder",
"size":200,
"children":[
{
"name":"$SPECS-APPLICATION",
"size":100
}
]
},
{
"name":"ServiceProvider",
"size":200,
"children":[
{
"name":"AgreementResponder",
"size":100
}
]
},
{
"name":"ServiceProvider",
"size":200,
"children":[
{
"name":"AgreementResponder",
"size":100
}
]
},
{
"name":"ExpirationTime",
"size":200,
"children":[
{
"name":"2014-02-02T06:00:00",
"size":100
}
]
},
{
"name":"ExpirationTime",
"size":200,
"children":[
{
"name":"2014-02-02T06:00:00",
"size":100
}
]
},
{
"name":"TemplateName",
"size":200,
"children":[
{
"name":"Y2-APP-TEMPLATE",
"size":100
}
]
},
{
"name":"TemplateName",
"size":200,
"children":[
{
"name":"Y2-APP-TEMPLATE",
"size":100
}
]
}
]
}
]
}
]
So, I ask you for a script be able to handle a generic JSON (unknown structure and deep) and create another JSON written with the above structure.
Thanks!

Try it at https://jsfiddle.net/zqkdq5mf/1/
function AppendObject(obj) {
//console.log(JSON.stringify(obj) + ":" + JSON.stringify(obj).length);
var Summary = null;
for (var propertyName in obj) {
if (typeof obj[propertyName] === "object" && !Summary) {
Summary = { name: propertyName.toString(), size: JSON.stringify(obj).length, children: [] };
for (var childPropertyName in obj[propertyName]) {
var Child = {};
Child[childPropertyName] = obj[propertyName][childPropertyName];
Summary.children[Summary.children.length] = AppendObject(Child);
}
break;
} else if (!Summary) Summary = { key: propertyName, value: obj[propertyName], size: JSON.stringify(obj).length };
}
return Summary;
}
function Convert() {
$(".data").empty();
var ParsedJSON = $.parseJSON($("textarea").val()),
ObjectArray = [],
Summaries = [];
if (!(ParsedJSON instanceof Array)) ObjectArray[0] = ParsedJSON;
else ObjectArray = ParsedJSON;
for (var i = 0; i < ObjectArray.length; i++) Summaries[Summaries.length] = AppendObject(ObjectArray[i]);
$(".data").text(JSON.stringify(Summaries));
}

Related

Restructure Nested JSON Data And Removing Some Keys

I need to restructure a nested JSON data.
Here is how it looks like:
{
"MainKey1": [
{
"Section1": {
"ParentTag1 Mapped Label": {
"ParentTag1": {
"Tag1 Mapped Label": {
"Tag1": "1234567890"
}
}
}
}
},
{
"Section2": {
"ParentTag1 Mapped Label": {
"ParentTag1": {
"Tag1 Label": {
"Tag1": "111222333444"
},
"Tag2 Label": {
"Tag2": "121212"
},
"Tag3 Label": {
"Tag3": "0987654321"
}
}
}
}
}
],
"MainKey2": [
{
"Section1": {
"ParentTag1 Mapped Label": {
"ParentTag1": {
"Tag1 Mapped Label": {
"Tag1": "1234567890"
}
}
}
}
}
]
}
And this is a sample of the converted JSON:
{
MainKey: [
{
Section1: [
{
ParentTag1: [
{ Tag1: "1234567890" }
]
}
]
},
{
Section2: [
{
ParentTag1: [
{ Tag1: "111222333444" },
{ Tag2: "121212" },
{ Tag3: "0987654321" }
]
}
]
}
],
MainKey2: [
{
Section1: [
{
ParentTag1 : [
{ Tag1: "1234567890" }
]
}
]
}
]
}
Rules:
Everything inside a MainKey (outermost keys, could be any name) should be an array
All labels should be stripped (as the label could be any name, without the actual word "Label", we can determine if it is a label based on the depth level. Since the JSON will have the label as the parent and the actual "tag" as a child.
Here is what I currently have (it is a mess, sorry!)
function convertJson (jsonObj) {
const mainKeys = Object.keys(jsonObj)
let output = {}
for (let i = 0; i < mainKeys.length; i++) {
const mainKey = mainKeys[i]
let result = []
output[mainKey] = result
for (let j = 0; j < jsonObj[mainKey].length; j++) {
const innerObj = {...jsonObj[mainKey][j]}
const sectionName = Object.keys(innerObj)[0]
const sectionObj = {}
sectionObj[sectionName] = []
const index = result.push(sectionObj) - 1
parseObj(innerObj[sectionName], result[index], 0) // if I change 2nd param to: result, it generates incorrect output
}
}
console.log(output)
}
function parseObj (innerObj, result, depthCount) {
for (var key in innerObj) {
if (typeof innerObj[key] === "object") {
if (depthCount % 2 === 1) {
const parentObj = {}
parentObj[key] = []
result.push(parentObj)
depthCount++
parseObj(innerObj[key], parentObj[key], depthCount)
} else {
depthCount++
parseObj(innerObj[key], result, depthCount)
}
} else {
const keyValuePairObj = {}
keyValuePairObj[key] = innerObj[key]
result.push(keyValuePairObj)
}
}
return result
}
convertJson(json)
But it generates an error:
Uncaught TypeError: result.push is not a function
Now if I change line 90 from:
parseObj(innerObj[sectionName], result[index], 0)
to:
parseObj(innerObj[sectionName], result, 0)
Here is incorrect output:
{
"MainKey1": [
{
"Section1": []
},
{
"ParentTag1": [
{
"Tag1": "1234567890"
}
]
},
{
"Section2": []
},
{
"ParentTag1": [
{
"Tag1": "111222333444"
},
{
"Tag2 Label": [
{
"Tag2": "121212"
}
]
},
{
"Tag3": "0987654321"
}
]
}
],
"MainKey2": [
{
"Section1": []
},
{
"Tag1": "1234567890"
}
]
}
And here is my fiddle:
https://jsfiddle.net/kzaiwo/L4avxmyd/36/
Thanks a lot! Appreciate any help!

frame array of recursive json object from an another array of objects

i have an array of objects of the below format
each with a unique 'sub-task' entry, each of this sub-task is to be embedded as a children element of each unique 'task' from the 'tasks' array
[
{
"sub-task":"abc",
"task":"alpha1"},
{
"sub-task":"def",
"task":"alpha1"},
{
"sub-task":"ijkl",
"task":"proto"},
{
"sub-task":"mno",
"task":"def"},
{
"sub-task":"qrs",
"task":"proto"},
{
"sub-task":"asdf",
"task":"mno"},
]
i was trying to frame an another array of below format
[
{
"name":"alpha1",
"children":[
{
"name":"abc"
},
{
"name":"def",
"children":[
{
"name":"mno"
}
]
}
]
},
{
"name":"proto",
"children":[
{
"name":"ijkl"
},
{
"name":"qrs",
"children":[
{
"name":"asdf"
}
]
}
]
}
]
i was trying of below logic, but ended up with no solution...
var treeData = [];
for( var ele of tasks){
recurOn(treeData,ele);
}
function recurOn(arr,obj){
if(arr.length == 0){
treeData.push({name:obj.parentGroup,children:[{name:obj.groupName}]})
//console.log(treeData);
return 1;
}else {
for(var item of treeData){
if(item.name == obj.parentGroup){
//console.log('item: ', item);
item.children.push({name:obj.groupName});
break;
}
else {
treeData.push(recurOn([],obj))
}
}
return 1;
}
}
//console.log(treeData);
//console.log(result);
Since the no of levels an elements holds is not known i was unable to fix for a logic
Use a map to store object reference.
let input = [
{ "sub-task": "abc", "task": "alpha1" },
{ "sub-task": "def", "task": "alpha1" },
{ "sub-task": "ijkl", "task": "proto" },
{ "sub-task": "mno", "task": "def" },
{ "sub-task": "qrs", "task": "proto" },
{ "sub-task": "asdf", "task": "mno" },
];
let map = new Map, result = [];
input.forEach(({ ["sub-task"]: name, task }) => {
let node = map.get(task), child = { name, children: [] };
if (!node) {
map.set(task, node = { name: task, children: [] });
result.push(node);
}
map.set(name, child);
node.children.push(child);
})
console.log(result);

how to improve this function that converts a flat array into a tree?

I have this function that converts a flat array to a tree based on a path property.
This is my data :
const input = [
{"name":"brussels_district_north","path":"Brussels/Brussels CBD/North"},
{"name":"brussels_district_louise","path":"Brussels/Brussels CBD/Louise"},
{"name":"brussels_district_west","path":"Brussels/Brussels Decentralised/West"},
{"name":"brussels_district_léopold","path":"Brussels/Brussels CBD/Léopold"},
{"name":"brussels_district_airport","path":"Brussels/Brussels Periphery/Airport"},
{"name":"brussels_district_ring","path":"Brussels/Brussels Periphery/Ring"},
{"name":"brussels_district_walloon-brabant","path":"Brussels/Brussels Decentralised/Walloon Brabant"},
{"name":"brussels_district_centrum","path":"Brussels/Brussels CBD/Centre"},
{"name":"brussels_district_midi","path":"Brussels/Brussels CBD/Midi"},
{"name":"brussels_district_south","path":"Brussels/Brussels Decentralised/South"},
{"name":"brussels_district_ north_east","path":"Brussels/Brussels Decentralised/North East"},
]
Then the "transform" function :
const output = [];
//make a tree out of this data
input.reduce((r, item) => {
item.path.split(/(?=\/)/).reduce((o, _, i, p) => {
o.children = o.children || [];
var path = p.slice(0, i + 1).join('');
var pathLast = path.match(/([^\/]*)\/*$/)[1] //list item of path splitted with '/'
var temp = o.children.find(o => o.path === path);
if (!temp) {
o.children.push(temp = { path });
}
return temp;
}, r);
return r;
}, { children: output });
console.log(output);
Which give me this :
[
{
"path":"Brussels",
"children":[
{
"path":"Brussels/Brussels CBD",
"children":[
{
"path":"Brussels/Brussels CBD/North"
},
{
"path":"Brussels/Brussels CBD/Louise"
},
{
"path":"Brussels/Brussels CBD/Léopold"
},
{
"path":"Brussels/Brussels CBD/Centre"
},
{
"path":"Brussels/Brussels CBD/Midi"
}
]
},
{
"path":"Brussels/Brussels Decentralised",
"children":
[
{
"path":"Brussels/Brussels Decentralised/West"
},
{
"path":"Brussels/Brussels Decentralised/Walloon Brabant"
},
{
"path":"Brussels/Brussels Decentralised/South"
},
{
"path":"Brussels/Brussels Decentralised/North East"
}
]
},
{
"path":"Brussels/Brussels Periphery",
"children":
[
{
"path":"Brussels/Brussels Periphery/Airport"
},
{
"path":"Brussels/Brussels Periphery/Ring"
}
]
}
]
}
]
That is nice !
But the output only has the path attribute, while I would like to keep the other ones (actually, there is only another name attribute here but I will have more), so I rather want this :
[
{
"path":"Brussels",
"children":[
{
"path":"Brussels/Brussels CBD",
"children":[
{
"path":"Brussels/Brussels CBD/North",
"name":"brussels_district_north"
},
{
"path":"Brussels/Brussels CBD/Louise",
"name":"brussels_district_louise"
},
{
"path":"Brussels/Brussels CBD/Léopold",
"name":"brussels_district_léopold"
},
{
"path":"Brussels/Brussels CBD/Centre",
"name":"brussels_district_centrum"
},
{
"path":"Brussels/Brussels CBD/Midi",
"name":"brussels_district_midi"
}
]
},
{
"path":"Brussels/Brussels Decentralised",
"children":
[
{
"path":"Brussels/Brussels Decentralised/West",
"name":"brussels_district_west"
},
{
"path":"Brussels/Brussels Decentralised/Walloon Brabant",
"name":"brussels_district_walloon-brabant"
},
{
"path":"Brussels/Brussels Decentralised/South",
"name":"brussels_district_south"
},
{
"path":"Brussels/Brussels Decentralised/North East",
"name":"brussels_district_ north_east"
}
]
},
{
"path":"Brussels/Brussels Periphery",
"children":
[
{
"path":"Brussels/Brussels Periphery/Airport",
"name":"brussels_district_airport"
},
{
"path":"Brussels/Brussels Periphery/Ring",
"name":"brussels_district_ring"
}
]
}
]
}
]
How could I edit my function to achieve this ?
Thanks
You could check if the index plus one is equal to the length of the splitted path and add name.
const
input = [{ name: "brussels_district_north", path: "Brussels/Brussels CBD/North" }, { name: "brussels_district_louise", path: "Brussels/Brussels CBD/Louise" }, { name: "brussels_district_west", path: "Brussels/Brussels Decentralised/West" }, { name: "brussels_district_léopold", path: "Brussels/Brussels CBD/Léopold" }, { name: "brussels_district_airport", path: "Brussels/Brussels Periphery/Airport" }, { name: "brussels_district_ring", path: "Brussels/Brussels Periphery/Ring" }, { name: "brussels_district_walloon-brabant", path: "Brussels/Brussels Decentralised/Walloon Brabant" }, { name: "brussels_district_centrum", path: "Brussels/Brussels CBD/Centre" }, { name: "brussels_district_midi", path: "Brussels/Brussels CBD/Midi" }, { name: "brussels_district_south", path: "Brussels/Brussels Decentralised/South" }, { name: "brussels_district_ north_east", path: "Brussels/Brussels Decentralised/North East" }],
output = [];
input.reduce((r, item) => {
item.path.split(/(?=\/)/).reduce((o, _, i, p) => {
o.children = o.children || [];
var path = p.slice(0, i + 1).join('');
var temp = o.children.find(o => o.path === path);
if (!temp) {
o.children.push(temp = { path });
if (i + 1 === p.length) temp.name = item.name;
}
return temp;
}, r);
return r;
}, { children: output });
console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How can I extract objects several layers deep using the map method?

I'm a trying to access the change order information as seen below to create a list of all the change_order_names. Using the current code I am getting the results I have posted. Will someone show me how I can access the change order information and produce the list?
if (componentType == CHANGE_ORDER_TYPE) {
if (!this.props.data) {
return null;
} else {
const changeList = this.props.data.map(function (result) {
return (result.change_orders);
});
// return resultsList;
console.log(changeList);
}
}
This is the current response from the map method above.
[
[
{
"id":1,
"change_order_name":"change 1",
"project_id":"1",
"cws_change_order_id":"33214324",
"slug":"change-1",
"created_at":null,
"updated_at":null
},
{
"id":2,
"change_order_name":"change 2",
"project_id":"1",
"cws_change_order_id":"3211324",
"slug":"change-2",
"created_at":null,
"updated_at":null
}
],
[
{
"id":3,
"change_order_name":"change 3",
"project_id":"2",
"cws_change_order_id":"3234324",
"slug":"change-3",
"created_at":null,
"updated_at":null
},
{
"id":4,
"change_order_name":"change 4",
"project_id":"2",
"cws_change_order_id":"6234532",
"slug":"change-4",
"created_at":null,
"updated_at":null
}
],
[
{
"id":5,
"change_order_name":"change 5",
"project_id":"3",
"cws_change_order_id":"3124214",
"slug":"change-5",
"created_at":null,
"updated_at":null
}
]
]
This is how I dug down to get get access to the individual change_orders
if (componentType == CHANGE_ORDER_TYPE) {
if (!this.props.data) {
return null;
} else {
const changeList = this.props.data.map(function (result) {
return (result.change_orders);
});
let changeOrdersArr = [];
for (let i = 0; i < changeList.length; i++) {
let inner = changeList[i];
for (let z = 0; z < inner.length; z++) {
changeOrdersArr.push(inner[z])
}
}
It's not entirely clear what your desired output is, but I think you want a single array with all change_orders in it? If this is correct then you can use the reduce function after your current map function:
if (componentType == CHANGE_ORDER_TYPE) {
if (!this.props.data) {
return null;
} else {
const changeList = this.props.data.map(function (result) {
return (result.change_orders);
}).reduce((output, item) => [...output, ...item], []);
// return changeList;
console.log(changeList);
}
}
const mapOutput = [
[
{
"id":1,
"change_order_name":"change 1",
"project_id":"1",
"cws_change_order_id":"33214324",
"slug":"change-1",
"created_at":null,
"updated_at":null
},
{
"id":2,
"change_order_name":"change 2",
"project_id":"1",
"cws_change_order_id":"3211324",
"slug":"change-2",
"created_at":null,
"updated_at":null
}
],
[
{
"id":3,
"change_order_name":"change 3",
"project_id":"2",
"cws_change_order_id":"3234324",
"slug":"change-3",
"created_at":null,
"updated_at":null
},
{
"id":4,
"change_order_name":"change 4",
"project_id":"2",
"cws_change_order_id":"6234532",
"slug":"change-4",
"created_at":null,
"updated_at":null
}
],
[
{
"id":5,
"change_order_name":"change 5",
"project_id":"3",
"cws_change_order_id":"3124214",
"slug":"change-5",
"created_at":null,
"updated_at":null
}
]
];
const change_orders = mapOutput.reduce((orders, arr) => [...orders,...arr],[]);
console.log(change_orders);

need to convert complex json obj to suitable angularjs ui tree data json structure

In my angularjs project I have original json in the following format :
$scope.orgJsonObj = {
"parentNodesList":[
"0",
"1",
"1.1",
"2",
"2.2"
],
"childNodes":{
"0":[
"1",
"2"
],
"1":[
"1.1",
"1.2"
],
"1.1":[
"1.1.1"
],
"2":[
"2.1",
"2.2"
],
"2.2":[
"2.2.1",
"2.2.3"
]
},
"nodeDetailsList":[
{
"id":"0",
"name":"node0"
},
{
"id":"1",
"name":"node1",
"parentId":"0"
},
{
"id":"2",
"name":"node2",
"parentId":"0"
},
{
"id":"1.1",
"name":"node1.1",
"parentId":"1"
},
{
"id":"1.2",
"name":"node1.2",
"parentId":"1"
},
{
"id":"1.1.1",
"name":"node1.1.1",
"parentId":"1.1"
},
{
"id":"2.1",
"name":"node2.1",
"parentId":"2"
},
{
"id":"2.2",
"name":"node2.2",
"parentId":"2"
},
{
"id":"2.2.1",
"name":"node2.2.1",
"parentId":"2.2"
},
{
"id":"2.2.3",
"name":"node2.2.3",
"parentId":"2.2"
}
]
}
Now I want to convert orgJsonObj json structure in to the tree structure similar to angular ui tree json data structure. In orgJsonObj, parentNodesList contains all parents in the tree. Each parent's children list is available in childNodes. Each nodes complete information is available in nodeDetailsList. The node obj { "id":"0", "name":"node0"} does not have parentId, property in it because it is the root of the tree.
After conversion my $scope.orgJsonObj, should become as shown below (which is suitable for angularjs ui tree)
$scope.finalJsonObj = [
{
"id":"0",
"title":"node0",
"nodes":[
{
"id":"1",
"title":"node1",
"nodes":[
{
"id":"1.1",
"title":"node1.1",
"nodes":[
{
"id":"1.1.1",
"title":"node1.1.1",
"nodes":[
]
}
]
},
{
"id":"1.2",
"title":"node1.2",
"nodes":[
]
}
]
},
{
"id":"2",
"title":"node2",
"nodes":[
{
"id":"2.1",
"title":"node2.1",
"nodes":[
]
},
{
"id":"2.2",
"title":"node2.2",
"nodes":[
{
"id":"2.2.1",
"title":"node2.2.1",
"nodes":[
]
},
{
"id":"2.2.3",
"title":"node2.2.3",
"nodes":[
]
}
]
}
]
}
]
}
]
Can any one help me in this.
var originalData = {};
var treeData = transformNesting("0", originalData.childNodes);
var result = applyData(treeData, originalData.nodeDetailsList);
function transformNesting(root, nestedData){
var tree = {
id: root,
nodes: nestedData[root] ? nestedData[root].map(function(newRoot){ return transformNesting(newRoot, nestedData)}) : []
};
return tree;
}
function getNodeById(list, id){
return list.filter(function(item){
return item.id === id;
})[0];
}
function applyData(tree, config){
tree.title = getNodeById(config, tree.id).name;
tree.nodes = tree.nodes.map(function(node){return applyData(node, config)});
return tree;
}

Categories