Modifying tree structured data in JavaScript with given condition? - javascript

I have a tree data like this:
var tree = [
{
id:"11",
text: "Parent 1",
type:"Parent",
nodeSelected:false,
nodes: [
{
text: "Child 1",
parentId: "11",
id:"21",
nodeSelected:false,
type: "Child",
nodes: [
{
id:"36",
text: "Grandchild 1",
parentId: "21",
nodeSelected:false,
nodes:[],
type: "Grandchild"
},
{
id:"38",
text: "Grandchild 2",
parentId: "21",
nodes:[],
nodeSelected:false,
type: "Grandchild"
}
]
},
{
id:"43",
text: "Child 2",
nodeSelected:false,
parentId:"11",
type: "Child",
nodes: [
{
id:"52",
parentId:"43",
text: "Grandchild 1 of child 2",
nodeSelected:false,
nodes:[],
type: "Grandchild"
}
]
}
]
},
{
id:"46",
text: "Parent 2",
nodeSelected:false,
type: "Parent",
nodes:[]
},
{
id:"48",
text: "Parent 3",
nodeSelected:false,
type: "Parent",
node: [
{
id:"70",
text: "child 3",
parentId: "48",
type: "Child",
nodeSelected:false,
nodes:[]
}
]
}
];
All of the nodeSelected are false.
I have group of Ids in form of an array.
groupedIds=["11","21","43","52","48"];
I want to do nodeSelected property true based on groupId array with some condition.
the condition is such that if a parentId is mentioned along with its children ids then nodeSelected property of parent should remain
false and child's nodeSelected should be true (and its nodes nodeSelected should also be 'true').
Or else whole parent nodes nodeSelected should be true(along with it nodes).
So the result will be like this:
var resultArray = [
{
id:"11",
text: "Parent 1",
type:"Parent",
nodeSelected:false,
nodes: [
{
text: "Child 1",
parentId: "11",
id:"21",
nodeSelected:true,
type: "Child",
nodes: [
{
id:"36",
text: "Grandchild 1",
parentId: "21",
nodeSelected:true,
type: "Grandchild"
},
{
id:"38",
text: "Grandchild 2",
parentId: "21",
nodeSelected:true,
type: "Grandchild"
}
]
},
{
id:"43",
text: "Child 2",
nodeSelected:false,
parentId:"11",
type: "Child",
nodes: [
{
id:"52",
parentId:"43",
text: "Grandchild 1 of child 2",
nodeSelected:true,
type: "Grandchild"
}
]
}
]
},
{
id:"46",
text: "Parent 2",
nodeSelected:false,
type: "Parent"
},
{
id:"48",
text: "Parent 3",
nodeSelected:true,
type: "Parent",
node: [
{
id:"70",
text: "child 3",
parentId: "48",
type: "Child",
nodeSelected:true
}
]
}
];
(My try) Although incomplete but something like this we can do
tree.forEach((Parent) => {
if (groupedIds.includes(Parent.id)) {
let isSomeChildSelected = Parent.nodes.some((loc) => groupedIds.includes(loc.id));
if (isSomeChildSelected) {
Parent.nodes.forEach((child) => {
if (groupedIds.includes(child.id)) {
let isSomeGrandChildSelected = child.nodes.some((grandchild) => groupedIds.includes(grandchild.id));
if (isSomeGrandChildSelected) {
child.nodes.forEach((grandchild) => {
if (groupedIds.includes(grandchild.id)) {
grandchild.isSelected = true;
}
})
} else {
child.isSelected = true;
child.nodes.forEach((grandchild) => {
grandchild.isSelected = true;
})
}
}
})
} else {
Parent.isSelected = true;
Parent.nodes.forEach((child) => {
child.isSelected = true;
child.nodes.forEach((grandchild) => {
grandchild.isSelected = true;
})
})
}
}
})
The above tried method solves the issue to some extent, but it is a bit complex.
Any help would be much appreciated. Thanks!

I'm not going to do it for you (not out of malice, just because I'm not 100% sure I understand the question!), but I'll hopefully give you a big point in the right direction.
When traversing trees, 90% of the time you need something called recursion. Recursion looks like this:
function walk(items){
items.forEach(items => {
if(item.hasChildrenOrSomething){
// notice the function calls itself:
walk(item.children);
}
// Do something with item
});
}
walk(tree);
There is one important thing to note here, which is that if you do some logic after calling the function inside itself, all the children of the current item will have that logic applied. So, here we go – in your case you want something like this:
const walk = (branch) => {
branch.forEach(node => {
// Check if this object even has children:
if(node.hasOwnProperty('nodes') && node.nodes.length){
walk(node.nodes);
// Do your node.nodes.every check here
}
// Now check if this is in the array
});
}
walk(tree);
If you've used recursion before, sorry, this whole post was probably just super patronising and has missed the point of your question. If you haven't, then can I just say I am a) jealous of you for getting to experience the feeling of something so elegant for the first time and b) sorry for you for having to unravel the mind boggling nature of them.

This gives me output what I want(with recursion):
var tree = [
{
id:"11",
text: "Parent 1",
type:"Parent",
nodeSelected:false,
nodes: [
{
text: "Child 1",
parentId: "11",
id:"21",
nodeSelected:false,
type: "Child",
nodes: [
{
id:"36",
text: "Grandchild 1",
parentId: "21",
nodeSelected:false,
nodes:[],
type: "Grandchild"
},
{
id:"38",
text: "Grandchild 2",
parentId: "21",
nodes:[],
nodeSelected:false,
type: "Grandchild"
}
]
},
{
id:"43",
text: "Child 2",
nodeSelected:false,
parentId:"11",
type: "Child",
nodes: [
{
id:"52",
parentId:"43",
text: "Grandchild 1 of child 2",
nodeSelected:false,
nodes:[],
type: "Grandchild"
}
]
}
]
},
{
id:"46",
text: "Parent 2",
nodeSelected:false,
type: "Parent",
nodes:[]
},
{
id:"48",
text: "Parent 3",
nodeSelected:false,
type: "Parent",
nodes: [
{
id:"70",
text: "child 3",
parentId: "48",
type: "Child",
nodeSelected:false,
nodes:[]
}
]
}
];
groupedIds=["11","21","43","52","48"];
var selectAllNode=array=> array.forEach((value)=>{ value.nodeSelected=true; value.nodes? selectAllNode(value.nodes) : [] });
var getFilteredData = array => array.forEach((ele)=>{
if(groupedIds.includes(ele.id)){
let isSomeSelected = ele.nodes.some((val)=>groupedIds.includes(val.id));
if(isSomeSelected){
getFilteredData(ele.nodes);
} else {
ele.nodeSelected=true;
selectAllNode(ele.nodes);
}
}
});
getFilteredData(tree);
console.log(tree);

Related

Flatten nested array of objects JS

I have a nested array of objects and want to get only the child property elements of an array. This is just an example and the actual data will include a unique children property in separate indices in the array. I am only able to traverse the first array in the list.
Here is my implementation:
const headers = [{
id: "name1",
title: "Name 1",
children: [{
title: "Children 1",
child: [{
title: "Child 1",
onClick: "child1Click"
},
{
title: "Child 2",
onClick: "child2Click"
}
]
},
{
title: "CHildren 2",
child: [{
title: "Child 3",
id: "child3Click"
},
{
title: "Child 4",
id: "child4Click"
}
]
}
]
},
{
id: "name2",
title: "Name 2",
children: [{
title: "Children 3",
child: [{
title: "Child 5",
onClick: "child5Click"
},
{
title: "Child 6",
onClick: "child6Click"
}
]
},
{
title: "CHildren 4",
child: [{
title: "Child 7",
id: "child7Click"
},
{
title: "Child 8",
id: "child8Click"
}
]
}
]
},
{
id: "name3",
title: "Name 3"
},
{
id: "name4",
title: "Name 4"
}
]
console.log(_.flattenDeep(_.map(_.compact(_.map(headers, item => item.children))[0], item1 => item1.child)))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
Expected Output:
[
{
"title": "Child 1",
"onClick": "child1Click"
},
{
"title": "Child 2",
"onClick": "child2Click"
},
{
"title": "Child 3",
"id": "child3Click"
},
{
"title": "Child 4",
"id": "child4Click"
},
{
"title": "Child 5",
"onClick": "child5Click"
},
{
"title": "Child 6",
"onClick": "child6Click"
},
{
"title": "Child 7",
"id": "child7Click"
},
{
"title": "Child 8",
"id": "child8Click"
}
]
Please advice.
Edit: I was able to get the required result using console.log(.flattenDeep(.map(.flattenDeep(.compact(_.map(headers, 'children'))), 'child')))
But is there an optimized version for doing the same? Thanks
Get the children with _.flatMap(), filter out the undefined values, and then use _.flatMap() again to get the values of the child property:
const headers = [{"id":"name1","title":"Name 1","children":[{"title":"Children 1","child":[{"title":"Child 1","onClick":"child1Click"},{"title":"Child 2","onClick":"child2Click"}]},{"title":"CHildren 2","child":[{"title":"Child 3","id":"child3Click"},{"title":"Child 4","id":"child4Click"}]}]},{"id":"name2","title":"Name 2","children":[{"title":"Children 3","child":[{"title":"Child 5","onClick":"child5Click"},{"title":"Child 6","onClick":"child6Click"}]},{"title":"CHildren 4","child":[{"title":"Child 7","id":"child7Click"},{"title":"Child 8","id":"child8Click"}]}]},{"id":"name3","title":"Name 3"},{"id":"name4","title":"Name 4"}]
const result = _.flatMap(_.compact(_.flatMap(headers, 'children')), 'child')
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
If you're using lodash/fp, you can generate a more readable function with _.flow():
const fn = _.flow(
_.flatMap('children'),
_.compact,
_.flatMap('child')
)
const headers = [{"id":"name1","title":"Name 1","children":[{"title":"Children 1","child":[{"title":"Child 1","onClick":"child1Click"},{"title":"Child 2","onClick":"child2Click"}]},{"title":"CHildren 2","child":[{"title":"Child 3","id":"child3Click"},{"title":"Child 4","id":"child4Click"}]}]},{"id":"name2","title":"Name 2","children":[{"title":"Children 3","child":[{"title":"Child 5","onClick":"child5Click"},{"title":"Child 6","onClick":"child6Click"}]},{"title":"CHildren 4","child":[{"title":"Child 7","id":"child7Click"},{"title":"Child 8","id":"child8Click"}]}]},{"id":"name3","title":"Name 3"},{"id":"name4","title":"Name 4"}]
const result = fn(headers)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
Using lodash with _.flow() and _.partialRight():
const pr = _.partialRight;
const fn = _.flow(
pr(_.flatMap, 'children'),
_.compact,
pr(_.flatMap, 'child')
)
const headers = [{"id":"name1","title":"Name 1","children":[{"title":"Children 1","child":[{"title":"Child 1","onClick":"child1Click"},{"title":"Child 2","onClick":"child2Click"}]},{"title":"CHildren 2","child":[{"title":"Child 3","id":"child3Click"},{"title":"Child 4","id":"child4Click"}]}]},{"id":"name2","title":"Name 2","children":[{"title":"Children 3","child":[{"title":"Child 5","onClick":"child5Click"},{"title":"Child 6","onClick":"child6Click"}]},{"title":"CHildren 4","child":[{"title":"Child 7","id":"child7Click"},{"title":"Child 8","id":"child8Click"}]}]},{"id":"name3","title":"Name 3"},{"id":"name4","title":"Name 4"}]
const result = fn(headers)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Filtering out objects of a deeply nested data structure in Typescript / Javascript

I want to filter out of a deeply nested data structure all the objects that match a rule. I know there are similar examples to delete a particular key of nested object, but I haven't been able to implement the solutions. The data structure will never be infinite, but it can be very deep (more than 10 levels)
Here are the types:
export type DataStructure = Entry[]
export interface Entry {
id: string
text: string
type: 1 | 2
children?: Entry[]
}
So an entry array could be:
[
{
id: "1",
type: 1,
text: "Node 1",
children: [
{
id: "1.1",
type: 1,
text: "Node 1.1",
children: [
{
id: "1.1.1",
type: 2,
text: "Node 1.1.1",
children: []
}
],
},
{
id: "1.2",
type: 1,
text: "Node 1.2",
children: [
{
id: "1.2.1",
type: 2,
text: "Node 1.2.1",
children: [],
},
{
id: "1.2.2",
type: 1,
text: "Node 1.2.2",
children: [],
}
]
}
]
}
]
The expected output array, provided the rule type !== 2 would be:
[
{
id: "1",
type: 1,
text: "Node 1",
children: [
{
id: "1.2",
type: 1,
text: "Node 1.2",
children: [
{
id: "1.2.2",
type: 1,
text: "Node 1.2.2",
children: [],
}
]
}
]
}
]
I haven't been able to implement recursion to do it, the arrays keep showing all the entries
I don't mind using lodash if there is a built-in function. Thanks!
If you want to filter based on type:
function test() {
const test = [
{
id: "1",
type: 1,
text: "Node 1",
children: [
{
id: "1.1",
type: 1,
text: "Node 1.1",
children: [
{
id: "1.1.1",
type: 1,
text: "Node 1.1.1",
children: []
}
],
},
{
id: "1.2",
type: 2,
text: "Node 1.2",
children: [
{
id: "1.2.1",
type: 2,
text: "Node 1.2.1",
children: [],
},
{
id: "1.2.2",
type: 2,
text: "Node 1.2.2",
children: [],
}
]
}
]
}
];
console.log(JSON.stringify(filterData(test, 2), 0, 4))
console.log('test: ', test);
}
and filter method:
function filterData(data, type) {
var r = data.filter(function(o) {
if (o.children)
o.children = filterData(o.children, type);
return o.type != type
})
return r;
}

convert nested object into recursive array or nested array in javascript es6

I worked nested object which I want to convert into recursive array or nested array.
I tried iterate the object something like below but it was creating single object of array.
Can anyone give suggestions or share your ideas it will helpful for me
iterate(obj) {
for (let property in obj) {
this.configArray.push({key: property,children: [], isValue: false, value: ''});
if (obj.hasOwnProperty(property)) {
const index = Object.keys(obj).indexOf(property);
if (typeof obj[property] == "object") {
this.iterate(obj[property]);
}
else {
this.configArray[index].children.push({ key: property, value: obj[property], isValue: true, children: [] });
}
}
}
}
INPUT
{
"Parent 1": {
"11": "Child 1",
"12": "Child 2",
},
"Parent 2": {
"20": {
"21": "Grand Child 1",
"22": "Grand Child 2",
}
},
"Parent 3": {
"31": "Child 1",
"32": "Child 2",
}
}
OUTPUT
[
{
key: "Parent 1",
value: "",
children: [
{ key: 11, value: "Child 1", children: [] },
{ key: 12, value: "Child 2", children: [] }
]
},
{
key: "Parent 2",
value: "",
children: [
{
key: 20,
value: "",
children: [
{ key: 21, value: "Grand Child 1", children: [] },
{ key: 22, value: "Grand Child 2", children: [] }
]
}
]
},
{
key: "Parent 3",
value: "",
children: [
{ key: 31, value: "Child 1", children: [] },
{ key: 32, value: "Child 2", children: [] }
]
},
];
You could take a recursive approach and map the value or the children, if value is an object.
function transform(object) {
return Object
.entries(object)
.map(([key, value]) => Object.assign({ key }, value && typeof value === 'object'
? { value: '', children: transform(value) }
: { value, children: [] }
));
}
var data = { "Parent 1": { 11: "Child 1", 12: "Child 2" }, "Parent 2": { 20: { 21: "Grand Child 1", 22: "Grand Child 2" } }, "Parent 3": { 31: "Child 1", 32: "Child 2" } },
result = transform(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

checkbox = true still won't show checkboxes in fancytree

I have this fancy tree
$('#my_id').fancytree({
checkbox: true,
selectMode: 3,
source :myDataSource,
extensions: ['filter'],
quicksearch: true,
});
myDataSource = [
{
title: "Everything",
id:"11",
folder:true,
children:[
{
title: "Node 1",
id: "1"
},
{
title: "Folder 2",
id: "2",
folder: true,
children: [
{
title: "Node 2.1",
id: "3",
myOwnAttr: "abc"
},
{
title: "Node 2.2",
id: "4"
}
]
}
]
}
]
But the fancytree is not showing checkboxes to its nodes. What am I missing?
Also when I try to access the selected nodes using $('#my_id').fancytree('getTree').getSelectedNodes();
, it throws an error.

Javascript Tree Lables/ Levels

var myArray = [{
title: "Title 1",
children: [{
title: "Title 1.1",
children: [{
title: "Title 1.1.1"
}]
}, {
title: "Title 1.2",
children: []
}]
},
{
title: "Title 2",
children: [{
title: "Title 2.1",
children: [{
title: "Title 2.1.1"
}]
}, {
title: "Title 2.2",
children: [{
title: "Title 2.2.1"
}]
}]
}
];
So I have an array that looks similar to this.
The titles are random but what I want to get is the level eg
{ title: 'Title 2.2 (or any othe random title)', level: '2.2', children:[...]}
I would like this to be recursive and a lodash solution would be highly appreciated.
Thanks.
You could use an iterative and recursive approach.
function addLevel(array, levels) {
levels = levels || [];
array.forEach(function (o, i) {
o.level = levels.concat(i + 1).join('.');
if (Array.isArray(o.children)) {
addLevel(o.children, levels.concat(i + 1));;
}
});
}
var myArray = [{ title: "Title 1", children: [{ title: "Title 1.1", children: [{ title: "Title 1.1.1" }] }, { title: "Title 1.2", children: [] }] }, { title: "Title 2", children: [{ title: "Title 2.1", children: [{ title: "Title 2.1.1" }] }, { title: "Title 2.2", children: [{ title: "Title 2.2.1" }] }] }];
addLevel(myArray);
console.log(myArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories