How to form nested objects in Javascript - javascript

I am trying to dynamically form a nested tree object something like below using JavaScript, can someone please let me know the best way to achieve this?
var contextpath= {
text: "TreeRoot",
items: [ {
text: "subgroup1" ,
items: [ {
text: "subgroup2",
items: [ {
text: "subgroup3",
items: [ {
text: "subgroup4",
items: [ {
text: "subgroup5"
}]
}]
}]
}]
}]
};
I have delimited string that I am trying to convert to object (that can be used as dat source for tree component).
var path="TreeRoot|subgroup1|subgroup2";
Trying to implement something like below but with recursion / looping using less number of variables.
var contextpathText= {};
contextpathText.text ="TreeRoot";
var childObject={};
var items=[];
childObject.text ="subgroup1";
items.push(childObject);
contextpathText.items=(items);

You need a depth counter and to store the current levels of the object you're working with.
var obj = {text:''}, parent, child, i = 0;
obj.text = 'TreeRoot';
parent = obj;
while (++i <= 5) {
if (parent.items === undefined) parent.items = []; // because you don't have an items on the last subgroup you can't put it in the object literal
child = {text: 'subgroup'+i};
parent.items.push(child);
parent = child;
}
parent = child = null; // cleanup
obj;
jsbeautified JSON.stringify(obj) is now
{
"text": "TreeRoot",
"items": [{
"text": "subgroup1",
"items": [{
"text": "subgroup2",
"items": [{
"text": "subgroup3",
"items": [{
"text": "subgroup4",
"items": [{
"text": "subgroup5"
}]
}]
}]
}]
}]
}
Edit For delimited string
var path = 'TreeRoot|subgroup1|subgroup2';
var obj = {text:''}, parent, child, levelText = path.split('|').reverse();
obj.text = levelText.pop() || '';
parent = obj;
while (levelText.length > 0) {
child = {text: levelText.pop()};
if (!parent.items) parent.items = [];
parent.items.push(child);
parent = child;
}
obj;

Beat me to it, but I went with this code:
var contextpath = { text: "TreeRoot", items: []}
var items = contextpath.items;
for(var i = 1; i <= 5; i++) {
items.push({ text: "subgroup" + i, items: []});
items = items[0].items;
}
The parent & child nomenclature is definitely clearer, for this sort of thing, but I wanted to show that you didn't have to declare the new object as a variable first, you can just push the object literal.
Whoops, just now noticed that you don't have an items array in your desired structure. My code creates the spare, so you end up with
// snip
text: "subgroup4",
items: [ {
text: "subgroup5",
items: []
}]
// etc.

Related

Tree Structure From Underscore Delimited Strings

Good day,
I need to convert strings as such:
Process1_Cat1_Cat2_Value1
Process1_Cat1_Cat2_Value2
Process2_Cat1_Cat2_Value1
into a nested array as such:
var d = [{
text: 'Process1',
children: [{
text: 'Cat1',
children: [{
text: 'Cat2',
children: [{
text: 'Value1'
},
{
text: 'Value2'
}]
}]
}]
},
{
text: 'Process2',
children: [{
text: 'Cat1',
children: [{
text: 'Cat2',
children: [{
text: 'Value1'
}]
}]
}]
},
];
The reason why I need to do this is to make use of a treeview to display my data:
https://www.npmjs.com/package/bootstrap-tree-view
I have looked at the following solution but was not able to get it working due to lowdash library throwing errors on the findWhere function:
Uncaught TypeError: _.findWhere is not a function
http://brandonclapp.com/arranging-an-array-of-flat-paths-into-a-json-tree-like-structure/
See below for the code:
function arrangeIntoTree(paths, cb) {
var tree = [];
// This example uses the underscore.js library.
_.each(paths, function(path) {
var pathParts = path.split('_');
pathParts.shift(); // Remove first blank element from the parts array.
var currentLevel = tree; // initialize currentLevel to root
_.each(pathParts, function(part) {
// check to see if the path already exists.
var existingPath = _.findWhere(currentLevel, {
name: part
});
if (existingPath) {
// The path to this item was already in the tree, so don't add it again.
// Set the current level to this path's children
currentLevel = existingPath.children;
} else {
var newPart = {
name: part,
children: [],
}
currentLevel.push(newPart);
currentLevel = newPart.children;
}
});
});
cb(tree);
}
arrangeIntoTree(paths, function(tree) {
console.log('tree: ', tree);
});
Any help will be appreciated!
You could use an iterative by looking for the text at the actual level. If not found create a new object. Return the children array for the next level until the most nested array. Then add the leaf object.
var data = ['Process1_Cat1_Cat2_Value1', 'Process1_Cat1_Cat2_Value2', 'Process2_Cat1_Cat2_Value1'],
result = data.reduce((r, s) => {
var keys = s.split('_'),
text = keys.pop();
keys
.reduce((q, text) => {
var temp = q.find(o => o.text === text);
if (!temp) {
q.push(temp = { text, children: [] });
}
return temp.children;
}, r)
.push({ text });
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

JSON data transformation from table-like format to JSON?

I have data that's in this format:
{
"columns": [
{
"values": [
{
"data": [
"Project Name",
"Owner",
"Creation Date",
"Completed Tasks"
]
}
]
}
],
"rows": [
{
"values": [
{
"data": [
"My Project 1",
"Franklin",
"7/1/2015",
"387"
]
}
]
},
{
"values": [
{
"data": [
"My Project 2",
"Beth",
"7/12/2015",
"402"
]
}
]
}
]
}
Is there some super short/easy way I can format it like so:
{
"projects": [
{
"projectName": "My Project 1",
"owner": "Franklin",
"creationDate": "7/1/2015",
"completedTasks": "387"
},
{
"projectName": "My Project 2",
"owner": "Beth",
"creationDate": "7/12/2015",
"completedTasks": "402"
}
]
}
I've already got the column name translation code:
r = s.replace(/\%/g, 'Perc')
.replace(/^[0-9A-Z]/g, function (x) {
return x.toLowerCase();
}).replace(/[\(\)\s]/g, '');
Before I dive into this with a bunch of forEach loops, I was wondering if there was a super quick way to transform this. I'm open to using libraries such as Underscore.
function translate(str) {
return str.replace(/\%/g, 'Perc')
.replace(/^[0-9A-Z]/g, function (x) {
return x.toLowerCase();
})
.replace(/[\(\)\s]/g, '');
}
function newFormat(obj) {
// grab the column names
var colNames = obj.columns[0].values[0].data;
// create a new temporary array
var out = [];
var rows = obj.rows;
// loop over the rows
rows.forEach(function (row) {
var record = row.values[0].data;
// create a new object, loop over the existing array elements
// and add them to the object using the column names as keys
var newRec = {};
for (var i = 0, l = record.length; i < l; i++) {
newRec[translate(colNames[i])] = record[i];
}
// push the new object to the array
out.push(newRec);
});
// return the final object
return { projects: out };
}
DEMO
There is no easy way, and this is really not that complex of an operation, even using for loops. I don't know why you would want to use regex to do this.
I would start with reading out the column values into a numerically indexed array.
So something like:
var sourceData = JSON.parse(yourJSONstring);
var columns = sourceData.columns[0].values[0].data;
Now you have a convenient way to start building your desired object. You can use the columns array created above to provide property key labels in your final object.
var sourceRows = sourceData.rows;
var finalData = {
"projects": []
};
// iterate through rows and write to object
for (i = 0; i < sourceRows.length; i++) {
var sourceRow = sourceRows[i].values.data;
// load data from row in finalData object
for (j = 0; j < sourceRow.length; j++) {
finalData.projects[i][columns[j]] = sourceRow[j];
}
}
That should do the trick for you.

Modify a JavaScript Object while iterating its properties

Can javascript implement pass-by-reference techniques on function call? You see, I have the JSON below and I need to traverse all its node. While traversing, if the current item is an Object and contains key nodes, I must add another property isParent: true to that exact same item. But I'm having difficulty on creating a traversal function with such feature, and I tried to search for traversal functions, but all I found only returns a new JSON object instead of changing the exact JSON that is being processed.
var default_tree = [
{
text: "Applications",
nodes: [
{
text: "Reports Data Entry",
nodes: [
{ text: "Other Banks Remittance Report" },
{ text: "Statement of Payroll Deduction" },
...
]
},
{
text: "Suspense File Maintenance",
nodes: [
{ text: "Banks with Individual Remittances" },
{ text: "Employers / Banks with Employers" },
...
]
}
]
},
{
text: "Unposted Transactions",
nodes: [
{ text: "Unposted Borrower Payments"},
{ text: "Unposted CMP Payments"}
]
},
{ text: "Maintenance" },
{
text: "Reports",
nodes: [
{
text: "Daily Reports",
nodes: [
{
text: "List of Remittance Reports",
nodes: [
{ text: "Banks" },
...
{
text: "Employers-LBP",
nodes: [
{ text: "Employers-Zonal" }
]
},
]
},
...
]
},
...
]
}
]
Considering we have this traversal function:
function traverse(json_object) {
// perform traversal here
}
traverse(default_tree)
After it runs the traverse function, the default_tree's value will remain the same unless we do something like:
default_tree = traverse(default_tree)
Can someone help me create an iterator will really alter the Object being processed while iterating, instead of returning a new Object?
Please check this one
var default_tree = [....] //Array
function traverse(arrDefaultTree){
arrDefaultTree.forEach(function(val,key){
if(val.hasOwnProperty("nodes")){
val.isParent = true;
traverse(val.nodes);
}
})
}
traverse(default_tree);
console.log(default_tree);
Hope this helpful.
With two functions, one that call then self. One find the nodes and the other one loop througgh the arrays.
traverseTree(default_tree);
function traverseTree (tree) {
var i = 0, len = tree.length;
for(;i < len; i++) {
var obj = tree[i];
findNodes(obj);
}
}
function findNodes (obj) {
var keys = Object.keys(obj);
if (keys.indexOf('nodes') > -1) {
obj.isParent = true;
traverseTree(obj.nodes);
}
}

Building an recursive tree from another

I have an problem with building an recursive tree from another. The function seems to work but if you expand the first array you'll notice that there is an endless recursion at index 2.
My function which builds the tree:
var buildTree = function(data, idx, aparent){
var parent = aparent || [];
for(var i = 0, c = data.length ; i < c ; i++){
parent.push({
text: data[i].text,
items: []
});
if(typeof data[i].items !== 'undefined' && data[i].items.length > 0){
var t = buildTree(data[i].items, idx + 1, parent[parent.length - 1].items);
parent[parent.length - 1].items.push(t);
}
}
return parent;
};
And that's how my tree data looks like:
[{
text: "house",
groupId: "1",
type: "group",
items: [{
text: "basement",
groupId: "2",
type: "group"
},{
text: "flat-1",
groupId: "3",
type: "group",
items: [{
text: "computer"
}]
}]
},{
text: "other-house",
groupId: "4",
type: "group"
}];
I think i has something to do that javascript returns the value by reference...
Here's a plunk with the complete data, check the console to after clicking the button to get an idea what i mean.
I can't really get my head around your code. Maybe your problem has something to do with the fact that you pass in the items-array during your recursion.
I have fixed up your code - making it a bit more simple and easy to read. It relies on the property items being an array if present, so if that is not always the case you need to add error handling for this scenario.
function recursiveBuildTree(data) {
var result = [];
data.forEach(function(item) {
var newItem = {
text: item.text,
items: item.items ? recursiveBuildTree(item.items) : []
};
result.push(newItem);
});
return result;
}

How to add property to nested javascript array (of objects)?

I am trying to add a property to javascript nested array object...
I need to traverse the tree get the value of text property and convert it to lowercase and add this data as new property (lowerText)
Old array:
var oldObject= [{
text: "Subgroup3",
items: [{
text: "subgroup5",
items: [{
text: "subgroup6",
items: [{
text: "subgroup7",
items: [{
text: "subgroup8"
}]
}]
}]
}]
}]
I need the new array object as below:
var newObject= [{
text: "Subgroup3",
lowerText:"subgroup3",
items: [{
text: "subgroup5",
lowerText:"subgroup5",
items: [{
text: "subgroup6",
lowerText:"subgroup6",
items: [{
text: "subgroup7",
lowerText:"subgroup7",
items: [{
text: "subgroup8",
lowerText:"subgroup8",
}]
}]
}]
}]
}]
This is what I tried, looping through each object and passing the items (array) to recursive function to set the property but it doesn't seem to work fine. Not sure what I am doing wrong, can someone please help me with this code?
for (var i = 0; i < data.length; i++) {
data[i].lowerText=data[i].text.toLowerCase();
loopTree(data[i].items);
}
function loopTree(node){
if (node) {
$.each(node, function (idx, item) {
item.lowerText=item.text.toLowerCase();
if(item.items){
loopTree(item.items)
}
});
}
}
EDIT: Below code did the job.
for (var i = 0; i < data.length; i++) {
if(data[i]){
process( data[i]);
}
}
function process(val) {
val.lowerText = val.text.toLowerCase();
if(val.items){
for(var i = 0, len = val.items.length; i < len; i++) {
process(val.items[i]);
}
}
}
If you don't want to clone the objects and just modify the existing ones, try this:
function process(val) {
val.lowerText = val.text.toLowerCase();
for(var i = 0, len = val.items.length; i < len; i++) {
process(val.items[i]);
}
}
process(obj);
If you want to preserve the value in the old array, just push the new object onto the old array:
oldObject.push(newObject[0]);
If you just need to replace the entire array, its trivial,
oldObject = newObject;

Categories