I have the data like this:
{
{
"text" : "parent1",
"nodes" :[
{
"text": "child1",
"nodes": [
{
"text": "grandchild1",
"nodes":[
{
"text": "hello",
"nodes": []
}
]
},
{
"text": "hello",
"nodes":[]
}
]
}
]
},
{
"text" : "parent2",
"nodes" :[
{
"text": "child2",
"nodes": [
{
"text": "grandchild2",
"nodes": [
{
"text": "grandgrandchild1",
"nodes": [
{
"text": "hello",
"nodes": []
}
]
}
]
}
]
}
]
}
}
What I want is, creating a path array that contains the path of the elements whose "text" value is "hello". For example, according to this data:
var paths: any[][] = [
["parent1","child1","grandchild1","hello"],
["parent1","child1","hello"],
["parent2","child2","grandchild2","grandgrandchild1","hello"]
];
I want this "paths" array. Please notice, if the element's text value is "hello", then this element's "nodes" length is 0. I think I'm making a mistake in recursion.
You need to
Traverse the object using recursion,
Assign paths along the way
and finally keep record those paths when the current text matches the text.
Demo
var obj = [{
"text": "parent1",
"nodes": [{
"text": "child1",
"nodes": [{
"text": "grandchild1",
"nodes": [{
"text": "hello",
"nodes": []
}]
},
{
"text": "hello",
"nodes": []
}
]
}]
},
{
"text": "parent2",
"nodes": [{
"text": "child2",
"nodes": [{
"text": "grandchild2",
"nodes": [{
"text": "grandgrandchild1",
"nodes": [{
"text": "hello",
"nodes": []
}]
}]
}]
}]
}
];
var helloPaths = [];
var valueToSearch = "hello";
function traverseAndCreatePath( obj, parent, valueToSearch )
{
if ( Array.isArray( obj ) )
{
obj.forEach( function(item){
traverseAndCreatePath( item, parent, valueToSearch );
});
}
else
{
if ( parent )
{
obj.path = parent.path.slice();
obj.path.push( obj.text );
}
else
{
obj.path = [ obj.text ];
}
if ( obj.text == valueToSearch )
{
helloPaths.push( obj.path.slice() );
}
if ( obj.nodes && obj.nodes.length )
{
obj.nodes.forEach( function(item){
traverseAndCreatePath( item, obj, valueToSearch );
});
}
}
return obj;
}
traverseAndCreatePath( obj, null, valueToSearch );
console.log( helloPaths );
This is a Depth First Traversal
var counter = 0;
var finalArray = []
function test(arr, node) {
arr.push(node.text);
if (node.hasOwnProperty("nodes") && Array.isArray(node.nodes) && node.nodes.length != 0) {
node.nodes.forEach(function(nodeChild) {
test(arr, nodeChild);
})
} else {
finalArray[counter] = arr.slice();
counter++;
}
arr = arr.slice(0, -1);
}
var b =[ { "text" : "parent1", "nodes" :[ { "text": "child1", "nodes": [ { "text": "grandchild1", "nodes":[ { "text": "hello", "nodes": [] } ] }, { "text": "hello", "nodes":[] } ] } ] }, { "text" : "parent2", "nodes" :[ { "text": "child2", "nodes": [ { "text": "grandchild2", "nodes": [ { "text": "grandgrandchild1", "nodes": [ { "text": "hello", "nodes": [] } ] } ] } ] } ] } ]
b.forEach(function(nodeVal) {
test([], nodeVal)
})
console.log(finalArray);
Your function should recieve:
A list of nodes of any length
And return:
A list of paths of any length
For the base of this behavior, we'll use reduce. It loops over the list once, and can keep adding to the result when needed.
const helloPathsFromNodes = (nodes = [], paths = []) =>
nodes.reduce(
/* TODO */ ,
paths
)
For each node, you check whether it's the end of an individual path to determine if you need to recurse. To keep track of the current path, we'll need to pass along an additional parameter (path) and add to it along the way:
if (node.text === "hello") return [...path, "hello"]
else return goDeeper([...path, node.text])
Putting this together, you get:
const helloPathsFromNodes = (nodes = [], paths = [], path = []) =>
nodes.reduce(
(acc, { text, nodes }) =>
text === "hello" ?
[...acc, [...path, text]] :
helloPathsFromNodes(nodes, acc, [...path, text]),
paths
);
console.log(helloPathsFromNodes(getData()));
// Example Data
function getData() { return [ { "text" : "parent1", "nodes" :[ { "text": "child1", "nodes": [ { "text": "grandchild1", "nodes":[ { "text": "hello", "nodes": [] } ] }, { "text": "hello", "nodes":[] } ] } ] }, { "text" : "parent2", "nodes" :[ { "text": "child2", "nodes": [ { "text": "grandchild2", "nodes": [ { "text": "grandgrandchild1", "nodes": [ { "text": "hello", "nodes": [] } ] } ] } ] } ] } ] };
Related
I am trying to reformat some relatively unstructured json to fit my needs, where I want each object to have a 'name' attribute as well as a 'children' array for sub-objects. I have a json file that looks like below:
{
"varA":true,
"varB":false,
"varC": {
"time":"15:00:00",
"date":"Jul 10",
"items":["apple", banana"]
}
}
I would like to format it to be something like this:
{
"name": "data",
"children": [
{
"name": "varA",
"children": [
{"name": "true"}
]
},
{
"name": "varB",
"children": [
{"name": "false"}
]
},
{
"name": "varC",
"children": [
{
"name": "time",
"children": [
{"name": "15:00:00"}
]
},
{
"name": "date",
"children": [
{"name": "Jul 10"}
]
},
{
"name": "items",
"children": [
{"name": "apple"},
{"name": "banana"}
]
}
]
}
]
}
How would I be able to do this recursively using pure js, in the case that objects can have a different depths of subobjects? Or is it still possible to do it iteratively?
Thanks in advance!!!
const data = {
"varA":true,
"varB":false,
"varC": {
"time":"15:00:00",
"date":"Jul 10",
"items":["apple", "banana"]
}
}
function parse(key, val) {
if (val === undefined) return { name: key };
if (typeof val !== "object") {
return {
name: key,
children: [
parse(val)
]
}
}
return {
name: key,
children: Object.entries(val).map(([newKey, newVal]) => parse(newKey, newVal))
}
}
const parsedData = parse("data", data)
I have the following JSON tree representation:
let tree = [
{
"label": "Org01",
"children": [
{
"label": "Dist01",
"children": [
{
"label": "School1",
"children": [
{
"label": "class1",
"children": []
},
{
"label": "class2",
"children": []
}
]
},
{
"label": "School1tst",
"children": []
}
]
},
{
"label": "Dist02",
"children": []
}
]
},
{
"label": "contoso01",
"children": [
{
"label": "Dist A",
"children": [
{
"label": "School A",
"children": [
{
"label": "classA",
"children": []
}
]
},
{
"label": "School B",
"children": [
{
"label": "classB",
"children": []
}
]
}
]
},
{
"label": "Dist B",
"children": [
{
"label": "School1",
"children": [
{
"label": "class1",
"children": []
}
]
}
]
}
]
}
];
I have a list of nodes in an array like here:
let whitelist = ['class1', 'School1', 'Dist01'];
How can I delete all the nodes from the tree which do not exist in the above array. However parent nodes need to be displayed on the tree if their children are in the whitelist.
Deleting a specific node from the tree is possible for me, but I could not figure out a way to delete all nodes from the tree except the few which are in the array.
Final output required:
output = [
{
"label": "razor01",
"children": [
{
"label": "Dist01",
"children": [
{
"label": "School1",
"children": [
{
"label": "class1",
"children": []
}
]
}
]
}
]
},
{
"label": "contoso01",
"children": [
{
"label": "Dist B",
"children": [
{
"label": "School1",
"children": [
{
"label": "class1",
"children": []
}
]
}
]
}
]
}];
Thanks and I appreciate any help with this.
const prunedNode = node => {
const pruned = whitelist.includes(node.label) ? node : null;
if (pruned) {
node.children = node.children.reduce((prunedChildren, child) => {
const prunedChildNode = prunedNode(child);
if (prunedChildNode) {
prunedChildren.push(prunedChildNode);
}
return prunedChildren;
}, []);
}
return pruned;
};
console.log(prunedNode(tree));
This should get the job done:
function filter(tree, list){
let output = [];
for(i in tree){
if(list.indexOf(tree[i].label) >= 0){
tree[i].children = filter(tree[i].children, list);
output.push(tree[i]);
}else{
output = output.concat(filter(tree[i].children, list));
}
}
return output;
}
I solved it this way:
function deleteNodes(tree, list) {
if (tree.length > 0) {
tree.forEach((node, i) => {
this.deleteNodes(node.subItems, list);
if (node.subItems) {
if (node.subItems.length === 0 && !list.includes(node.text))
{
tree.splice(i, 1);
}
}
});
}
}
Below is my JSON:
var records =
[
{
"name": "Node-1",
"nodes": [
{
"name": "Node-1-1",
"parentName" : null,
"nodes": [
{
"name": "Node-1-1-1",
"parentName" : null,
"nodes": []
}
]
}
]
}
];
Now I want that each child record to have its parent name in parentName property.
For example: parentName for Node-1-1 should be Node-1 because Node-1 is parent of Node-1-1
But here I am not getting that how would I will know that Node-1 is parent of Node-1-1 and how I would access Node-1 value when I am reading Node-1-1 record?
Expected output:
[
{
"name": "Node-1",
"nodes": [
{
"name": "Node-1-1",
"parentName" : "Node-1",
"nodes": [
{
"name": "Node-1-1-1",
"parentName" : "Node-1-1",
"nodes": []
}
]
}
]
}
];
var records =
[
{
"name": "Node-1",
"nodes": [
{
"name": "Node-1-1",
"parentName" : null,
"nodes": [
{
"name": "Node-1-1-1",
"parentName" : null,
"nodes": []
}
]
}
]
}
];
Iterate(records);
function Iterate(nodes)
{
nodes.forEach(function (node) {
if (node.nodes) {
Iterate(node.nodes);
}
});
}
You could pass in the parent as another parameter to the Iterate method.
var records = [{
"name": "Node-1",
"nodes": [{
"name": "Node-1-1",
"parentName": null,
"nodes": [{
"name": "Node-1-1-1",
"parentName": null,
"nodes": []
}]
}]
}];
Iterate(records);
function Iterate(nodes, parent) {
nodes.forEach(function(node) {
if (parent) {
node.parentName = parent.name;
}
if (node.nodes) {
Iterate(node.nodes, node);
}
});
}
console.log(records);
Just pass the parentName as parameter of the Iterate function.
var records = [
{
"name": "Node-1",
"nodes": [
{
"name": "Node-1-1",
"parentName" : null,
"nodes": [
{
"name": "Node-1-1-1",
"parentName" : null,
"nodes": []
}
]
}
]
}
];
Iterate(records);
console.log(records);
function Iterate(nodes, parentName) {
nodes.forEach(node => {
if (parentName) {
node.parentName = parentName;
}
if (node.nodes) {
Iterate(node.nodes, node.name);
}
});
}
I have a array like this->
var jsonResponse = [
{
"name": "abc",
"value": [
{ "label" : "Daily", "value":"Daily"}
]
},
{
"name": "ccc",
"value": [
{ "label" : "Daily", "value":"Daily"}
]
}
]
And I want to convert it to ->
{
"abc" : {
"name": "abc",
"value": [
{ "label" : "Daily", "value":"Daily"}
]
},
"ccc": {
"name": "ccc",
"value": [
{ "label" : "Daily", "value":"Daily"}
]
}
]
Probably I dont want foreach.
We can do partial with Object.assign( arrayDetails, ...jsonResponse);
But how to do object index?
let indexedResult = {};
jsonResponse.map(obj => indexedResult[obj.name] = obj)
console.log(JSON.stringify(indexedResult));
I have a tree of data and am trying to create a recursive function to add each path in the tree as an array of strings to better understand recursion. I am not sure why my method is not producing the expect
var tree = {
"name": "home",
"children": [
{
"name": "cars",
"children": [
{
"name": "ford",
"children": [
{
"name": "mustang"
},
{
"name": "explorer"
}
]
}
]
},
{
"name": "food",
"children": [
{
"name": "pizza"
}
]
}
]
};
var list = [];
var path = [];
function traverse(node) {
if (node.name) {
path.push(node.name)
}
if (!node.children) {
if (path.length) {
list.push(path);
}
return;
} else {
node.children.forEach(function(item) {
traverse(item);
});
}
}
traverse(tree);
console.log(list);
The output I am looking to create is:
[
["home"],
["home", "cars"],
["home", "cars", "ford"],
["home", "cars", "ford", "mustang"],
["home", "cars", "ford", "explorer"],
["home", "food"],
["home", "food", "pizza"]
]
You modify the same path array in all iterations. You should copy it instead:
var list = [];
function traverse(node, path) {
if ( !path )
path = [];
if (node.name) {
path.push(node.name)
}
list.push(path);
if (node.children) {
node.children.forEach(function(item) {
traverse(item, path.slice());
});
}
}
traverse(tree, []);
I have corrected your code, this solution copies the path variable from one function call to the other:
var tree = {
"name": "home",
"children": [{
"name": "cars",
"children": [{
"name": "ford",
"children": [{
"name": "mustang"
}, {
"name": "explorer"
}]
}]
}, {
"name": "food",
"children": [{
"name": "pizza"
}]
}]
};
var path = [];
var list = [];
function traverse(node, path) {
if ( !path )
path = [];
if (node.name) {
path.push(node.name)
}
list.push(path);
if (node.children) {
node.children.forEach(function(item) {
traverse(item, path.slice());
});
}
document.write(JSON.stringify(path )+ '<br>')
}
traverse(tree, []);