Filter with treeview - javascript

I am experiencing problems using a treeview filter. I have made following method:
var tree =
[{
id: "Arvore",
text: "Arvore",
children: [
{
id: "Folha_1",
text: "Folha 1",
children: [
{
id: "Folha_1_1",
text: "Folha 1.1",
children: [
{
id: "dd",
text: "abc"
}
]
}
]
},
{
id: "Folha_2",
text: "Folha 2"
},
{
id: "Folha_3",
text: "Folha 3"
},
{
id: "Folha_4",
text: "Folha 4"
},
{
id: "Folha_5",
text: "Folha 5"
}
]
}];
filterData: function filterData(data, value) {
return data.filter(function(item) {
if (item.children) item.children = filterData(item.children, value);
return item.text.indexOf(value) > -1;
});
},
But when I enter text, for example Folha 1.1, I want it to return Arvore > Folha 1 > Folha 1.1, but the function returns only the first children. What can i do?

Something like this should work. If you want to return the path then you need to check if the item matches OR if it has children left (meaning a child matched).
var tree =
[{
id: "Arvore",
text: "Arvore",
children: [
{
id: "Folha_1",
text: "Folha 1",
children: [
{
id: "Folha_1_1",
text: "Folha 1.1",
children: [
{
id: "dd",
text: "abc"
}
]
}
]
},
{
id: "Folha_2",
text: "Folha 2"
},
{
id: "Folha_3",
text: "Folha 3"
},
{
id: "Folha_4",
text: "Folha 4"
},
{
id: "Folha_5",
text: "Folha 5"
}
]
}];
function filterData(data, value) {
return data.filter(function(item) {
if (item.children) item.children = filterData(item.children, value);
return item.text.indexOf(value) > -1 || (item.children && item.children.length > 0);
});
}
filterData(tree, "Folha 1.1")
console.log(tree)

I found solution:
filterData: function filterData(data, value, forceShow) {
return data.filter(function(item) {
if (item.children) item.children = filterData(item.children, value, item.text.indexOf(value) > -1);
return forceShow || (item.text.indexOf(value) > -1 || (item.children && item.children.length > 0));
});
},

Related

How to remove Children Nodes and add them to parent Node by removing current Node?

I'm trying to organize a JSON data. Here is my JSON:
let all = [
{
id: "n1",
name: "Hipokrat",
children: [
{
id: "n2",
name: "Edward Janner",
children: [
{
id: "n9",
name: "Edison, Thomas",
}
]
}
]
},
{
id: "n3",
name: "William Harvey",
children: [
{
id: "n10",
name: "Lister, Joseph",
children: [
{
id: "n11",
name: "Kant, Immanuel",
children: [
{
id: "n15",
name: "Rawls, John"
},
{
id: "n46",
name: "More, Thomas",
},
{
id: "n47",
name: "Galen",
}
]
}
]
},
{
id: "n12",
name: "Smith, Adam",
}
]
},
{
id: "n48",
name: "Osler, William",
children: [
{
id: "n51",
name: "Louis Pasteur",
}
]
},
{
id: "n52",
name: "John Hunter",
children: [
{
id: "n53",
name: "Freud, Sigmund",
}
]
}
];
At here, I want to find the node with "n10" id and move them to the ancestor node which is "William Harvey" and with "n3" id. We should add these to children array.
So I want this result:
let all = [
{
id: "n1",
name: "Hipokrat",
children: [
{
id: "n2",
name: "Edward Janner",
children: [
{
id: "n9",
name: "Edison, Thomas",
}
]
}
]
},
{
id: "n3",
name: "William Harvey",
children: [
{
id: "n11",
name: "Kant, Immanuel",
children: [
{
id: "n15",
name: "Rawls, John"
},
{
id: "n46",
name: "More, Thomas",
},
{
id: "n47",
name: "Galen",
}
]
},
{
id: "n12",
name: "Smith, Adam",
}
]
},
{
id: "n48",
name: "Osler, William",
children: [
{
id: "n51",
name: "Louis Pasteur",
}
]
},
{
id: "n52",
name: "John Hunter",
children: [
{
id: "n53",
name: "Freud, Sigmund",
}
]
}
];
Here is my try:
let all = [
{
id: "n1",
name: "Hipokrat",
children: [
{
id: "n2",
name: "Edward Janner",
children: [
{
id: "n9",
name: "Edison, Thomas",
}
]
}
]
},
{
id: "n3",
name: "William Harvey",
children: [
{
id: "n11",
name: "Kant, Immanuel",
children: [
{
id: "n15",
name: "Rawls, John"
},
{
id: "n46",
name: "More, Thomas",
},
{
id: "n47",
name: "Galen",
}
]
},
{
id: "n12",
name: "Smith, Adam",
}
]
},
{
id: "n48",
name: "Osler, William",
children: [
{
id: "n51",
name: "Louis Pasteur",
}
]
},
{
id: "n52",
name: "John Hunter",
children: [
{
id: "n53",
name: "Freud, Sigmund",
}
]
}
];
function isObject(variable){
if ( typeof variable === 'object' && !Array.isArray(variable) && variable !== null) {
return true;
} else{
return false;
}
}
function isArray(variable){
if(!isObject(variable) && Array.isArray(variable)){
return true;
} else{
return false;
}
}
function process(all, value, indexes = "", foundAndChildren = false) {
if(foundAndChildren){
return foundAndChildren;
}
if(isArray(all)){ // is Array
console.log("-> Children size: " + all.length);
console.log("-PASSED WITH 1");
console.log("-----------------");
console.log("");
all.forEach(subElement => {
return process(subElement, value);
});
} else if(isObject(all)){ // is object
if(all["id"] == value){ // Match
console.log("#########");
console.log(all.id);
console.log(all.name);
console.log("--PASSED WITH 2");
let children = [];
if(all["children"] != undefined){
children = all.children
}
let res = {
id: value,
indexes: indexes,
children: children
};
console.log(res);
return res;
} else {
console.log(all.name);
console.log("--PASSED WITH 3");
console.log("");
if(isArray(all["children"])) { // is object but has children
console.log("->" + all.name);
console.log("Children size:" + all.children.length);
console.log("--PASSED WITH 4");
console.log("");
all["children"].forEach(elementOfChildren => {
return process(elementOfChildren, value);
});
}
}
}
}
console.log(process(all, "n10"));
And here is the playground: https://playcode.io/823402/
You can do something like this:
function replaceNode(jsonData, id) {
function replaceChildren(o, id) {
for (let key in o) {
const object = o[key]
if (object.id == id) {
if (object.children && object.children.length > 0) {
o.splice(key, 1, ...object.children)
return true
} else throw `id: ${id} has no children`
} else {
if (object.children && object.children.length > 0) {
const found = replaceChildren(object.children, id)
if (found) return found
}
}
}
}
const o = [...jsonData]
replaceChildren(o, id)
return o
}
and then pass the json data and the node id
const output = replaceNode(all, 'n10')

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>');

Nested array filtering, is there a more elegant way?

I'm trying to filter a nested structure, based on a search string.
If the search string is matched in an item, then I want to keep that item in the structure, along with its parents.
If the search string is not found, and the item has no children, it can be discounted.
I've got some code working which uses a recursive array filter to check the children of each item:
const data = {
id: '0.1',
children: [
{
children: [],
id: '1.1'
},
{
id: '1.2',
children: [
{
children: [],
id: '2.1'
},
{
id: '2.2',
children: [
{
id: '3.1',
children: []
},
{
id: '3.2',
children: []
},
{
id: '3.3',
children: []
}
]
},
{
children: [],
id: '2.3'
}
]
}
]
};
const searchString = '3.3';
const filterChildren = (item) => {
if (item.children.length) {
item.children = item.children.filter(filterChildren);
return item.children.length;
}
return item.id.includes(searchString);
};
data.children = data.children.filter(filterChildren);
console.log(data);
/*This outputs:
{
"id": "0.1",
"children": [
{
"id": "1.2",
"children": [
{
"id": "2.2",
"children": [
{
"id": "3.3",
"children": []
}
]
}
]
}
]
}*/
I'm concerned that if my data structure becomes massive, this won't be very efficient.
Can this be achieved in a 'nicer' way, that limits the amount of looping going on? I'm thinking probably using a reducer/transducer or something similarly exciting :)
A nonmutating version with a search for a child.
function find(array, id) {
var child,
result = array.find(o => o.id === id || (child = find(o.children, id)));
return child
? Object.assign({}, result, { children: [child] })
: result;
}
const
data = { id: '0.1', children: [{ children: [], id: '1.1' }, { id: '1.2', children: [{ children: [], id: '2.1' }, { id: '2.2', children: [{ id: '3.1', children: [] }, { id: '3.2', children: [] }, { id: '3.3', children: [] }] }, { children: [], id: '2.3' }] }] },
searchString = '3.3',
result = find([data], searchString);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Javascript Recursion on array and child arrays

Having a senior-moment, and struggling to get a recursive method to work correctly in Javascript.
There are similar Q&A's here, though nothing I see that has helped me so far.
That being said, if there is indeed a duplicate, i will remove this question.
Given the following array of objects:
var collection = [
{
id: 1,
name: "Parent 1",
children: [
{ id: 11, name: "Child 1", children: [] },
{ id: 12, name: "Child 2", children: [] }
]
},
{
id: 2,
name: "Parent 2",
children: [
{
id: 20,
name: "Child 1",
children: [
{ id: 21, name: "Grand Child 1", children: [] },
{ id: 22, name: "Grand Child 2", children: [] }
]
}
]
},
{
id: 3,
name: "Parent 3",
children: [
{ id: 31, name: "Child 1", children: [] },
{ id: 32, name: "Child 2", children: [] }
]
},
];
I've gone through a few attempts though my method seems to return early after going through one level only.
My latest attempt is:
Can someone please point me in the right direction.
function findType(col, id) {
for (i = 0; i < col.length; i++) {
if (col[i].id == id) {
return col[i];
}
if (col[i].children.length > 0) {
return findType(col[i].children, id);
}
}
return null;
}
I am trying to find an object where a given id matches, so looking for id 1 should return the whole object with the name Parent 1. If looking for id 31 then the whole object with the id 31 and name Child 1 should be returned.
This would translate into
var t = findType(collection, 1);
or
var t = findType(collection, 31);
Note I would like help with a pure JavaScript solution, and not a plugin or other library. Though they may be more stable, it won't help with the learning curve. Thanks.
You was close, you need a variable to store the temporary result of the nested call of find and if found, then break the loop by returning the found object.
Without, you return on any found children without iterating to the end of the array if not found at the first time.
function findType(col, id) {
var i, temp;
for (i = 0; i < col.length; i++) {
if (col[i].id == id) {
return col[i];
}
if (col[i].children.length > 0) {
temp = findType(col[i].children, id); // store result
if (temp) { // check
return temp; // return result
}
}
}
return null;
}
var collection = [{ id: 1, name: "Parent 1", children: [{ id: 11, name: "Child 1", children: [] }, { id: 12, name: "Child 2", children: [] }] }, { id: 2, name: "Parent 2", children: [{ id: 20, name: "Child 1", children: [{ id: 21, name: "Grand Child 1", children: [] }, { id: 22, name: "Grand Child 2", children: [] }] }] }, { id: 3, name: "Parent 3", children: [{ id: 31, name: "Child 1", children: [] }, { id: 32, name: "Child 2", children: [] }] }];
console.log(findType(collection, 31));
console.log(findType(collection, 1));
.as-console-wrapper { max-height: 100% !important; top: 0; }
const findType = (ar, id) => {
return ar.find(item => {
if (item.id === id) {
return item;
}
return item.children.find(cItem => cItem.id === id)
})
}
I think this suffice your needs
You need to ask for the "found" object
let found = findType(col[i].children, id);
if (found) {
return found;
}
Look at this code snippet
var collection = [{ id: 1, name: "Parent 1", children: [{ id: 11, name: "Child 1", children: [] }, { id: 12, name: "Child 2", children: [] } ] }, { id: 2, name: "Parent 2", children: [{ id: 20, name: "Child 1", children: [{ id: 21, name: "Grand Child 1", children: [] }, { id: 22, name: "Grand Child 2", children: [] } ] }] }, { id: 3, name: "Parent 3", children: [{ id: 31, name: "Child 1", children: [] }, { id: 32, name: "Child 2", children: [] } ] }];
function findType(col, id) {
for (let i = 0; i < col.length; i++) {
if (col[i].id == id) {
return col[i];
}
if (col[i].children.length > 0) {
let found = findType(col[i].children, id);
if (found) {
return found;
}
}
}
return null;
}
var t = findType(collection, 31);
console.log(t);
.as-console-wrapper {
max-height: 100% !important
}
1.
Actually your function findType return the value null for every id parameter at the node
{ id: 11, name: "Child 1", children: [] }
When you hit return, it stops all the recursion.
You have to check the return value from the nested call of findType function.
2.
Your for loop should looke like
for (let i = 0; i < col.length; i++)
instead of
for (i = 0; i < col.length; i++)
Because without the let you share a same variable i in the nested call of the function findType and the value will be changed for the dad calling function.
The function could be :
function findType(col, id) {
for (let i = 0; i < col.length; i++) {
if (col[i].id == id) {
return col[i];
}
var nested = findType(col[i].children, id);
if (nested) return nested;
}
return null;
}

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