How to access nested key on object with array JavaScript? - javascript

I want to make hierarchy role layout. So I want to access nested JavaScript array with in a object. Needs to add 'row' and 'column' key in every object based depth and index.
API response array like this:
const dataArray = [
{
name: "master name",
child: [
{
name: "child 1a",
child: [
{ name: "child 1a2a" },
{
name: "child 1a2b",
child: [
{ name: "child 2b3a" },
{
name: "child 2b3b",
child: [
{ name: "child 3b4a" },
{ name: "child 3b4b" },
{ name: "child 3b4c" },
],
},
{ name: "child 2b3c" },
],
},
{ name: "child 1a2c" },
],
},
{
name: "child 1b",
child: [
{ name: "child 1b2a" },
{ name: "child 1b2b" },
{ name: "child 1b2c" },
],
},
],
},
];
This is what I have tried, row key added following code, need help for column
const assignDepth = (arr, row = 0, index = 0) => {
if (index < arr.length) {
arr[index].row = row;
if (arr[index].hasOwnProperty("child")) {
return assignDepth(arr[index].child, row + 1, 0);
}
return assignDepth(arr, row, index + 1);
}
return;
};
await assignDepth(dataArray);
console.log(JSON.stringify(dataArray, undefined, 4));
Then my expected result array is:
const resultArray = [
{
name: "master name",
row: 0,
column: 0,
child: [
{
name: "child 1a",
row: 1,
column: 1,
child: [
{ name: "child 1a2a", row: 2, column: 1 },
{
name: "child 1a2b",
row: 2,
column: 2,
child: [
{ name: "child 2b3a", row: 3, column: 1 },
{
name: "child 2b3b",
row: 3,
column: 2,
child: [
{ name: "child 3b4a", row: 4, column: 1 },
{ name: "child 3b4b", row: 4, column: 2 },
{ name: "child 3b4c", row: 4, column: 3 },
],
},
{ name: "child 2b3c", row: 3, column: 3 },
],
},
{ name: "child 1a2c", row: 2, column: 3 },
],
},
{
name: "child 1b",
row: 1,
column: 2,
child: [
{ name: "child 1b2a", row: 2, column: 4 },
{ name: "child 1b2b", row: 2, column: 5 },
{ name: "child 1b2c", row: 2, column: 6 },
],
},
],
},
];
So how can I render this, Anyone help me out. Thanks in advance!

You can try the following logic:
var ranking = {0:0};
let addRowColumn = (data,row=0) =>{
data.forEach(item=>{
item.row = row;
item.column = ranking[row];
ranking[row]+=1;
if(item.child && item.child.length){
ranking[row+1] = ranking[row+1] || 1;
addRowColumn(item.child,row+1);
}
});
};
addRowColumn(dataArray);
console.log(dataArray);
This is tested. you can change the ranking object to start with a specific row and column. The key of object represents the row and value represents the column of data.

Related

I need to use javascript to turn a "flat" json object into a nested json tree with a 1 to many match ability

I need use javascript to turn a flat JSON object into a nested tree. The catch that I haven't yet seen in other solutions is that I need to have the ability to match 1 to many... Meaning that I sometimes have a child node that needs to fit into multiple parent nodes.
Here is my flat json object:
[{
id: 200,
title: "child with 2 parents",
parent_ids: [1, 2]
}, {
id: 202,
title: "child with 1 parent",
parent_ids: [1]
}, {
id: 1,
title: "parent 1",
children: []
}, {
id: 2,
title: "parent 2",
children: []
}]
I know that I can might be able to do something like loop through all the parents and children individually to match them up but that seems really inefficient and I'm wondering if there is a better way to go about this?
This is the ideal outcome
notice the child with 2 parents gets slotted into both parents
[
{
id: 1,
title: "parent 1",
children: [{
id: 200,
title: "child with 2 parents",
parent_ids: [1, 2]
}, {
id: 202,
title: "child with 1 parent",
parent_ids: [1]
}]
},
{
id: 2,
title: "parent 2",
children: [{
id: 200,
title: "child with 2 parents",
parent_ids: [1, 2]
}]
}
]
You could collect all nodes in a hash table and get all nodes without parents as result set.
function getTree(data) {
var temp = {},
parents = [];
data.forEach(o => {
o.children = temp[o.id] && temp[o.id].children;
temp[o.id] = o;
if (!o.parent_ids) {
parents.push(o.id);
return;
}
o.parent_ids.forEach(id => {
temp[id] = temp[id] || {};
temp[id].children = temp[id].children || [];
temp[id].children.push(o);
});
});
return parents.map(id => temp[id]);
}
var data = [{ id: 200, title: "child with 2 parents", parent_ids: [1, 2] }, { id: 202, title: "child with 1 parent", parent_ids: [1] }, { id: 1, title: "parent 1", children: [] }, { id: 2, title: "parent 2", children: [] }],
tree = getTree(data);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to convert json to tree array in JS?

I would like to convert this json / object to this specific structure below to allow me to use a treeList component.
I've tried to build a recursive function but I didn't find the solution yet.
Thanks for your help
const data = {
parent1: {
child1: { bar: "1" },
child2: "2"
},
parent2: {
child1: "1"
}
}
to
const treeData = [
{
title: "parent1",
key: "parent1",
children: [
{
title: "child1",
key: "child1",
children: [{ title: "bar", key: "bar", value: "1" }]
},
{
title: "child2",
key: "child2",
value: "2"
}
],
},
{
title: "parent2",
key: "parent2",
children: [
{
title: "child1",
key: "child1",
value: "1"
}
]
}
]
You could take an iterative and recursive approach.
function getNodes(object) {
return Object
.entries(object)
.map(([key, value]) => value && typeof value === 'object'
? { title: key, key, children: getNodes(value) }
: { title: key, key, value }
);
}
const data = { parent1: { child1: { bar: "1" }, child2: "2" }, parent2: { child1: "1" } },
result = getNodes(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
just share sample, a little different from yours. But it give you a hint with recursive function.
https://jsfiddle.net/segansoft/7bdxmys4/1/
function getNestedChildren(arr, parent) {
var out = []
for (var i in arr) {
if (arr[i].parent == parent) {
var children = getNestedChildren(arr, arr[i].id)
if (children.length) {
arr[i].children = children
}
out.push(arr[i])
}
}
return out
}
var flat = [{
id: 1,
title: 'hello',
parent: 0
},
{
id: 2,
title: 'hello',
parent: 0
},
{
id: 3,
title: 'hello',
parent: 1
},
{
id: 4,
title: 'hello',
parent: 3
},
{
id: 5,
title: 'hello',
parent: 4
},
{
id: 6,
title: 'hello',
parent: 4
},
{
id: 7,
title: 'hello',
parent: 3
},
{
id: 8,
title: 'hello',
parent: 2
}
]
var nested = getNestedChildren(flat, 0)
document.write('<pre>' + JSON.stringify(nested, 0, 4) + '</pre>');

JavaScript - Is there a simple way to get the values for every occurrence of a specific key in nested JSON

I have a JSON structure similar to this:
[
{
cells: [
{ id: "1", cellType: 3, widget: { id: 1, description: "myDesc"} },
{ id: "2", cellType: 4, widget: { id: 2, description: "myDesc2"} }
]
},
{
cells: [
{ id: "3", cellType: 5, widget: { id: 3, description: "myDesc3"} }
]
},
...
]
How do I get the value of every widget into a separate array using EcmaScript (or anything that's available in Angular 2+), and without using a library (including JQuery)? I need a final array like this:
[
{
id: 1,
description: "myDesc"
},
{
id: 2,
description: "myDesc2"
},
...
]
Update
(and thanks to #Felix Kling for the 1st part) - I found I can get all of the widgets with this:
JSON.parse(json)[0].forEach( c => c.cells.forEach( c2 => console.log(c2.widget)));
You can use .map() with .reduce()
let input = [
{
cells: [
{ id: "1", cellType: 3, widget: { id: 1, description: "myDesc"} },
{ id: "1", cellType: 4, widget: { id: 2, description: "myDesc2"} }
]
},
{
cells: [
{ id: "3", cellType: 5, widget: { id: 3, description: "myDesc3"} }
]
},
];
let result = input.reduce((result, current) => {
return result.concat(current.cells.map(x => x.widget));
}, [])
console.log(result);
You can use .map() and .concat() to get the desired result:
let data = [{
cells: [
{ id: "1", cellType: 3, widget: { id: 1, description: "myDesc"} },
{ id: "1", cellType: 4, widget: { id: 2, description: "myDesc2"} }
]}, {
cells: [
{ id: "3", cellType: 5, widget: { id: 3, description: "myDesc3"} }
]
}];
let result = [].concat(...data.map(({cells}) => cells.map(({widget}) => widget)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Javascript - How to compare and sort one array based on the order of second array?

If I have an array with a bunch of posts sorted any old how:
[
{
id: 1,
name: "first parent post"
},
{
id: 2,
name: "second child of first parent post"
},
{
id: 3,
name: "second parent post"
},
{
id: 4,
name: "first child of first parent post"
},
{
id: 5,
name: "first child of second parent post"
}
]
However, another array decides the structure of the first array based on the ids of the first array:
[
{
id: 1,
parent: 0
},
{
id: 4,
parent: 1
},
{
id: 2,
parent: 1
},
{
id: 3,
parent: 0
},
{
id: 5,
parent: 3
}
]
What would be the most efficient way to sort these so that the first array is sorted by the second array?
I would expect the resulting array to look something like this:
[
{
id: 1,
name: "first parent post",
indent: 0
},
{
id: 4,
name: "first child of first parent post",
indent: 1
},
{
id: 2,
name: "second child of first parent post",
indent: 1
},
{
id: 3,
name: "second parent post",
indent: 0
},
{
id: 5,
name: "first child of second parent post",
indent: 1
}
]
You can order your data array by index and then order it by the order array;
to get the indentation you'll have to track the parent's indentation upstream and add one:
var data = [
{
id: 1,
name: "first parent post"
},
{
id: 2,
name: "second child of first parent post"
},
{
id: 3,
name: "second parent post"
},
{
id: 4,
name: "first child of first parent post"
},
{
id: 5,
name: "first child of second parent post"
}
]
var ord = [
{
id: 1,
parent: 0
},
{
id: 4,
parent: 1
},
{
id: 2,
parent: 1
},
{
id: 3,
parent: 0
},
{
id: 5,
parent: 3
}
]
// first you arrange the data element by index
dataByIndex = data.reduce((ac, x) => {
ac[x.id] = x;
return ac
},[])
// then you order them by the ord array
var res = ord.reduce((ac, x, i) => {
var item = dataByIndex[x.id]
item.indent = x.parent === 0 ? 0 : getParentIndentPlusOne(ac, x.parent)
return [...ac, item]
}, [] )
function getParentIndentPlusOne(ac, id){
var i = ac.length
while (--i > -1){
if (id === ac[i].id) return ac[i].indent + 1
}
}
console.log(res)
You need to generate a tree with the dependencies first and then render the indent for all items of the orginal array.
This proposal works for unsorted data/relations as well.
function getDataWithIndent(data, relation) {
var hash = Object.create(null),
tree = function (data, root) {
var r = [], o = {};
data.forEach(function (a) {
a.children = o[a.id] && o[a.id].children;
o[a.id] = a;
if (a.parent === root) {
r.push(a);
} else {
o[a.parent] = o[a.parent] || {};
o[a.parent].children = o[a.parent].children || [];
o[a.parent].children.push(a);
}
});
return r;
}(relation, 0),
result = [];
data.forEach(function (a) {
hash[a.id] = a;
});
tree.forEach(function iter(indent) {
return function (a) {
hash[a.id].indent = indent;
result.push(hash[a.id]);
Array.isArray(a.children) && a.children.forEach(iter(indent + 1));
};
}(0));
return result;
}
var data = [{ id: 1, name: "first parent post" }, { id: 2, name: "second child of first parent post" }, { id: 3, name: "second parent post" }, { id: 4, name: "first child of first parent post" }, { id: 5, name: "first child of second parent post" }],
relation = [{ id: 1, parent: 0 }, { id: 4, parent: 1 }, { id: 2, parent: 1 }, { id: 3, parent: 0 }, { id: 5, parent: 3 }],
result = getDataWithIndent(data, relation);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
For the ordering, convert the first array into a Map, then pull the information from that into the first array.
function combineArrays(arr1, arr2) {
let names = new Map(arr1.map(({id, name}) => [id, name]));
return arr2.map(({id, parent}) => ({id: id, name: names.get(id), parent: parent}));
}
This will give you name, id, and parent in the order you want. Calculating indent is left as an exercise to the reader (or should probably be a different question).

Json: edit deeply nested value

I have an array of json like this:
var tree = [
{
text: "Parent 1",
id: 1,
nodes: [
{
text: "Child 1",
id: 2,
nodes: [
{
text: "Grandchild 1"
id: 3,
},
{
text: "Grandchild 2"
id: 4,
nodes: [
{
text: "Grandchild 3"
id: 10,
},
{
text: "Grandchild 4"
id: 11,
nodes: [
{
text: "Grandchild 5"
id: 12,
},
{
text: "Grandchild 6"
id: 13,
}
]
}
]
}
]
},
{
text: "Child 2"
id: 5,
}
]
},
{
text: "Parent 2"
id: 6,
},
{
text: "Parent 3"
id: 7,
},
{
text: "Parent 4"
id: 8,
},
{
text: "Parent 5"
id: 9,
}
];
I'm trying to create a function that would take as parameter the tree, and id, and a newText parameter, that would find the node with the given id, replace the text by newText, and return the modified json.
Ex:
editTree(tree, 11, "Granchild 13435")
Is there a way to achieve this ?
I don't know how to solve this since I need the path to the key in order to edit the tree.
You can use recursive function for this.
var tree = [{"text":"Parent 1","id":1,"nodes":[{"text":"Child 1","id":2,"nodes":[{"text":"Grandchild 1","id":3},{"text":"Grandchild 2","id":4,"nodes":[{"text":"Grandchild 3","id":10},{"text":"Grandchild 4","id":11,"nodes":[{"text":"Grandchild 5","id":12},{"text":"Grandchild 6","id":13}]}]}]},{"text":"Child 2","id":5}]},{"text":"Parent 2","id":6},{"text":"Parent 3","id":7},{"text":"Parent 4","id":8},{"text":"Parent 5","id":9}]
function editTree(tree, id, val) {
for (var i in tree) {
if (i == 'id') {
if (tree[i] == id) {
tree.text = val
return 1;
}
}
if (typeof tree[i] == 'object') editTree(tree[i], id, val)
}
return tree;
}
console.log(editTree(tree, 11, "Granchild 13435"))
You could use an iterative and recursive approach for searching the node. If found stop iteration and return.
This proposal uses Array#some, which allowes to exit the iteration.
If a node from an actual node exists and the node is an array, then this node gets iterated.
function editTree(tree, id, text) {
tree.some(function iter(o) {
if (o.id === id) {
o.text = text;
return true;
}
return Array.isArray(o.nodes) && o.nodes.some(iter);
});
}
var tree = [{ text: "Parent 1", id: 1, nodes: [{ text: "Child 1", id: 2, nodes: [{ text: "Grandchild 1", id: 3, }, { text: "Grandchild 2", id: 4, nodes: [{ text: "Grandchild 3", id: 10, }, { text: "Grandchild 4", id: 11, nodes: [{ text: "Grandchild 5", id: 12, }, { text: "Grandchild 6", id: 13, }] }] }] }, { text: "Child 2", id: 5, }] }, { text: "Parent 2", id: 6, }, { text: "Parent 3", id: 7, }, { text: "Parent 4", id: 8, }, { text: "Parent 5", id: 9, }];
editTree(tree, 11, "Granchild 13435");
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is a simple function to find a node matching a property and a value :
function findNode(nodes, prop, value) {
if(!value || !(nodes instanceof Array)) return;
for(var i=0; i<nodes.length; i++) {
if(node = (value == nodes[i][prop]) ? nodes[i] : findNode(nodes[i]['nodes'], value)) {
return node;
}
}
}
Then simply call :
// Find node with id = 10
var node = findNode(tree, 'id', 10);
if(node) {
// Yeah! we found it, now change its text
node['text'] = 'Changed!';
}
// Ensure tree has been updated
console.log(tree);
Sample snippet (check out that text of node with id = 10 has changed) :
var tree=[{text:"Parent 1",id:1,nodes:[{text:"Child 1",id:2,nodes:[{text:"Grandchild 1",id:3},{text:"Grandchild 2",id:4,nodes:[{text:"Grandchild 3",id:10},{text:"Grandchild 4",id:11,nodes:[{text:"Grandchild 5",id:12},{text:"Grandchild 6",id:13}]}]}]},{text:"Child 2",id:5}]},{text:"Parent 2",id:6},{text:"Parent 3",id:7},{text:"Parent 4",id:8},{text:"Parent 5",id:9}];
function findNode(nodes, prop, value) {
if(!value || !(nodes instanceof Array)) return;
for(var i=0; i<nodes.length; i++) {
var node = (value == nodes[i][prop]) ? nodes[i] : findNode(nodes[i]['nodes'], prop, value);
if(node ) {
return node;
}
}
}
var node = findNode(tree, 'id', 10);
if(node) {
node['text'] = 'Changed!';
}
console.log(tree)
I've created library that uses recursive walk and one of its method is exactly what you need.
https://github.com/dominik791/obj-traverse
Use findFirst() method. The first parameter is a root object, not array, so you should create it at first:
var tree = {
text: 'rootObj',
nodes: [
{
text: 'Parent 1',
id: 1,
nodes: [
{
'text': 'Child 1',
id: 2,
nodes: [ ... ]
},
{
'name': 'Child 2',
id: 3,
nodes: [ ... ]
}
]
},
{
text: 'Parent2',
id: 6
}
};
Then:
var objToEdit = findFirst(tree, 'nodes', { id: 11 });
Now objToEdit is a reference to the object that you want to edit. So you can just:
objToEdit.text = 'Granchild 13435';
And your tree is updated.

Categories

Resources