Find node by id in json tree - javascript

I want to add the children array to node where id = 52126f7d (or another). How do I do it?
var children = [
{ name: 'great-granchild3',
id: '2a12a10h'
},
{ name: 'great-granchild4',
id: 'bpme7qw0'
}
]
// json tree
var objects = {
name: 'all objects',
id:"2e6ca1c3",
children: [
{
name: 'child',
id: "6c03cfbe",
children: [
{ name: 'grandchild1',
id: "2790f59c"
},
{ name: 'grandchild2',
id: "52126f7d"
},
{ name: 'grandchild3',
id: "b402f14b"
},
{
name: 'grandchild4',
id: "6c03cff0",
children: [
{ name: 'great-grandchild1',
id: "ce90ffa6"
},
{ name: 'great-grandchild2',
id: "52f95f28"
}
]
}
]
},
{
name: 'child2',
id: "7693b310",
children: [
{ name: 'grandchild5',
id: "def86ecc"
},
{ name: 'grandchild6',
id: "6224a8f8"
}
]
}
]
}
to end up with
var objects = {
name: 'all objects',
id:"2e6ca1c3",
children: [
{
name: 'child',
id: "6c03cfbe",
children: [
{ name: 'grandchild1',
id: "2790f59c"
},
{ name: 'grandchild2',
id: "52126f7d",
children = [
{ name: 'great-granchild3',
id: '2a12a10h'
},
{ name: 'great-granchild4',
id: 'bpme7qw0'
}
]
},
{ name: 'grandchild3',
id: "b402f14b"
},
{
name: 'grandchild4',
id: "6c03cff0",
children: [
{ name: 'great-grandchild1',
id: "ce90ffa6"
},
{ name: 'great-grandchild2',
id: "52f95f28"
}
]
}
]
},
{
name: 'child2',
id: "7693b310",
children: [
{ name: 'grandchild5',
id: "def86ecc"
},
{ name: 'grandchild6',
id: "6224a8f8"
}
]
}
]
}

by finding the proper node first.
function getNodeById(id, node){
var reduce = [].reduce;
function runner(result, node){
if(result || !node) return result;
return node.id === id && node || //is this the proper node?
runner(null, node.children) || //process this nodes children
reduce.call(Object(node), runner, result); //maybe this is some ArrayLike Structure
}
return runner(null, node);
}
var target = getNodeById("52126f7d", objects);
target.children = children;

How about:
objects.children[0].children[1].children = children;

Related

Remove/Add all childrenNode of a tree from an array when clicking on a parentNode

I have this nested object :
export interface RenderTree {
id: string;
name: string;
children?: readonly RenderTree[];
}
export const UserLoginRoleV2Tree: RenderTree = {
id: "root",
name: "APP",
children: [
{
id: "1",
name: UserLoginRoleV2.DASHBOARD
},
{
id: "2",
name: UserLoginRoleV2.REPORTING,
children: [
{
id: "2.1",
name: UserLoginRoleV2.REPORTS
},
{
id: "2.2",
name: UserLoginRoleV2.SALES_DETAILS
},
{
id: "2.3",
name: UserLoginRoleV2.LEADS_DETAILS
}
]
},
{
id: "3",
name: UserLoginRoleV2.SALES_DATA,
children: [
{
id: "3.1",
name: UserLoginRoleV2.SALES
},
{
id: "3.2",
name: UserLoginRoleV2.LEADS
},
{
id: "3.3",
name: UserLoginRoleV2.CALLS
},
{
id: "3.4",
name: UserLoginRoleV2.PHONE_CLOSING
}
]
},
{
id: "4",
name: UserLoginRoleV2.TRACKING,
children: [
{
id: "4.1",
name: UserLoginRoleV2.UNIVERSAL_SCRIPT
},
{
id: "4.2",
name: UserLoginRoleV2.SOURCE_LINKS
},
{
id: "4.3",
name: UserLoginRoleV2.URL_RULES
},
{
id: "4.4",
name: UserLoginRoleV2.PRODUCTS
},
{
id: "4.4",
name: UserLoginRoleV2.TAGS
}
]
},
{
id: "5",
name: UserLoginRoleV2.SETTINGS,
children: [
{
id: "5.1",
name: UserLoginRoleV2.PROFILE
},
{
id: "5.2",
name: UserLoginRoleV2.BILLING
},
{
id: "5.3",
name: UserLoginRoleV2.SUB_USERS
},
{
id: "5.4",
name: UserLoginRoleV2.CONNECTED_ACCOUNTS
},
{
id: "5.5",
name: UserLoginRoleV2.INTEGRATIONS
},
{
id: "5.6",
name: UserLoginRoleV2.TRACKING_SETTINGS
},
{
id: "5.7",
name: UserLoginRoleV2.TRACKING_DOMAINS
}
]
}
]
}
From this object, I'm able to build a Tree. Each node of the tree has a checkbox. To know if the checkbox is checked or not, I have a state array when I keep all nodes that are checked :
interface State {
roles: Array<string>;
}
<TreeItem key={nodes.id} nodeId={nodes.id} label={<Checkbox checked={this.state.roles.includes(nodes.name)} onClick={...}>
{Array.isArray(nodes.children)
? nodes.children.map((node) => this.renderTree(node))
: null}
</TreeItem>
I'm looking for a way to remove all children of a node from the array when clicking on it, knowing that a child can have children too.
I feel that we could make it with a recursive function, but I don't have success with that for now. Can you help me?
Okay I resolved it with Depth-first search
/**
* Get all children of a node.
* #param branch The node whose children you want to know
* #param callback Function to apply on each node.
*/
const breadthFirstSearch = (branch: TreeBranch): TreeBranch[] => {
let queue = new Array(branch);
let explored = new Array(branch);
let res = new Array();
let b: TreeBranch;
while (queue.length !== 0) {
b = queue.shift();
res.push(b);
b.children?.forEach(child => {
if (!explored.includes(child)) {
queue.push(child);
explored.push(child);
}
})
}
return res;
}

multilevel object traversing and print value based on key

Here is my object. There could be children inside of children and so on.
I Am trying to print only name for all of them.
Here is JSFiddle
var products = [
{
name: 'Allison',
children: [
{
name: 'John',
children: [
{
name: 'Scott',
children: [],
},
],
},
{
name: 'Sarah',
children: [],
},
]
},
{
name: 'Tony',
children: [
{
name: 'Lucy',
children: [],
}
]
}
This is what I have tried so far, how can I Print name of all children regardless of what level they are in the object??
for(var i = 0; i < products.length; i++)
{
console.log(products[i].name);
if(products[i].children.length > 0) {
console.log(products[i].children);
// Print only name of all children.
}
}
You can use recursive technical.
var products = [ { name: 'Allison', children: [{ name: 'John', children: [ { name: 'Scott', children: [],},],},{ name: 'Sarah', children: [],},]},{name: 'Tony',children: [{name: 'Lucy',children: [],}]}];
const printRecursively = (products) => {
for (const k of products)
{
console.log(k.name);
k.children.length > 0 && printRecursively(k.children); // Recurive here.
}
}
printRecursively(products);
Here is the implementation, please check
Also here
var products = [
{
name: 'Allison',
children: [
{
name: 'John',
children: [
{
name: 'Scott',
children: [],
},
],
},
{
name: 'Sarah',
children: [],
},
]
},
{
name: 'Tony',
children: [
{
name: 'Lucy',
children: [],
}
]
}
];
function printChildrenNames(children) {
for(var i = 0; i < children.length; i++) {
console.log(children[i].name);
if(children[i].children.length > 0) {
printChildrenNames(children[i].children);
}
}
}
printChildrenNames(products)
Here is an iterative solution using object-scan. For your use case a simple recursive solution might be the best choice, however this solution is very clean and easily adjustable when requirements change.
// const objectScan = require('object-scan');
const data = [{ name: 'Allison', children: [{ name: 'John', children: [{ name: 'Scott', children: [] }] }, { name: 'Sarah', children: [] }] }, { name: 'Tony', children: [{ name: 'Lucy', children: [] }] }];
console.log(objectScan(['**.name'], { rtn: 'value', reverse: false })(data));
// => [ 'Allison', 'John', 'Scott', 'Sarah', 'Tony', 'Lucy' ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#14.0.0"></script>
Disclaimer: I'm the author of object-scan

JavaScript - transform object, consolidate

I'm trying to transform the object below. I need to create a new array of unique locations, with the location and item objects in each node.
With the help of JackOfAshes I was able to get halfway there in this PEN
Transform this:
const orig = [
{
item: {
name: "cat",
id: "ca_123"
},
location: {
name: "porch",
id: "por_123"
}
},
{
item: {
name: "dog",
id: "do_123"
},
location: {
name: "porch",
id: "por_123"
}
},
{
item: {
name: "snake",
id: "sn_123"
},
location: {
name: "forest",
id: "for_123"
}
},
{
item: {
name: "bird",
id: "bi_123"
},
location: {
name: "forest",
id: "for_123"
}
},
{
item: {
name: "beer",
id: "be_123"
},
location: {
name: "fridge",
id: "fri_123"
}
}
];
Into this:
const desired = [
{
name: "porch",
id: "por_123",
items: [
{
name: "cat",
id: "ca_123"
},
{
name: "dog",
id: "do_123"
}
]
},
{
name: "forest",
id: "for_123",
items: [
{
name: "snake",
id: "sn_123"
},
{
name: "bird",
id: "bi_123"
}
]
},
{
name: "fridge",
id: "fri_123",
items: [
{
name: "beer",
id: "be_123"
}
]
}
];
You can do it, or use reduce
const orig = [
{
item: {
name: "cat",
id: "ca_123"
},
location: {
name: "porch",
id: "por_123"
}
},
{
item: {
name: "dog",
id: "do_123"
},
location: {
name: "porch",
id: "por_123"
}
},
{
item: {
name: "snake",
id: "sn_123"
},
location: {
name: "forest",
id: "for_123"
}
},
{
item: {
name: "bird",
id: "bi_123"
},
location: {
name: "forest",
id: "for_123"
}
},
{
item: {
name: "beer",
id: "be_123"
},
location: {
name: "fridge",
id: "fri_123"
}
}
];
let formattedData = {}
orig.forEach(data=>{
if(!formattedData[data.location.id]) formattedData[data.location.id]= {
id: data.location.id,
name: data.location.name,
items:[]
}
formattedData[data.location.id].items.push(data.item)
})
const finalResponse = Object.entries(formattedData).map((e) => ( { ...e[1] } ));
console.log(finalResponse)

Remove items from nested array of objects based on a condition

In my application, I have data returned from the server like below. It has very deep nesting:
var data = [{
name: "root",
children: [{
name: "Parent1",
children: [{
name: "Parent1-child1",
children: [{
name: "Parent1-child1-grandchild1",
children: [{
name: "Parent1-child1-grandchild1-last",
children:[]
}]
},
{
name: "Parent1-child1-grandchild2",
children: []
},
{
name: "Parent1-child1-grandchild3",
children: []
}
]
},
{
name: "Paren1-child2",
children: [{
name: "Parent1-chil2-grandchild1",
children: []
},
{
name: "Parent1-child2-grandchild2",
children: [{
name: "Parent1-child2-grandchild2-last",
children: []
}]
},
{
name: "Parent1-child2-grandchild3",
children: []
}
]
},
{
name: "Parent1-child3",
children: []
}
]
},
{
name: "Parent2",
children: [{
name: "Parent2-child1",
children: []
},
{
name: "Parent2-child2",
children: [{
name: "Parent2-child2-grandchild1",
children: []
},
{
name: "Parent2-child2-grandchild2",
children: [{
name: "Parent2-child2-grandchild2-last",
children: []
}]
}
]
}
]
},
{
name: "Parent3",
children: []
}
]
}];
The requirement is to loop through all the objects (deep nested level also) and remove the object if the children property has a value of an empty array. So the output should be like below
var data = [{
name: "root",
children: [{
name: "Parent1",
children: [{
name: "Parent1-child1",
children: [{
name: "Parent1-child1-grandchild1",
children: []
},
]
},
{
name: "Paren1-child2",
children: [
{
name: "Parent1-child2-grandchild2",
children: []
},
]
},
]
},
{
name: "Parent2",
children: [
{
name: "Parent2-child2",
children: [
{
name: "Parent2-child2-grandchild2",
children: []
}
]
}
]
}
]
}];
I have tried the following code, but it doesn't work as expected. Please let me know how to achieve the expected result.
function checkChildrens(arr) {
arr.forEach((ele,i) => {
if(ele.hasOwnProperty('children')) {
checkChildrens(ele['children'])
} else {
arr.splice(i,1)
}
})
}
checkChildrens(data);
I have tried with the filter method also in that case. It is not working correctly.
arr.filter((ele,i)=>{
if(ele.hasOwnProperty('children') && ele.children.length !== 0 ){
removeEmpty(ele.children)
}else{
return false;
}
return true;
})
You could rebuild new objects by checking the children array length.
function filter(array) {
return array.reduce((r, o) => {
if (o.children && o.children.length) {
r.push(Object.assign({}, o, { children: filter(o.children) }));
}
return r;
}, []);
}
var data = [{ name: "root", children: [{ name: "Parent1", children: [{ name: "Parent1-child1", children: [{ name: "Parent1-child1-grandchild1", children: [{ name: "Parent1-child1-grandchild1-last", children: [] }] }, { name: "Parent1-child1-grandchild2", children: [] }, { name: "Parent1-child1-grandchild3", children: [] }] }, { name: "Paren1-child2", children: [{ name: "Parent1-chil2-grandchild1", children: [] }, { name: "Parent1-child2-grandchild2", children: [{ name: "Parent1-child2-grandchild2-last", children: [] }] }, { name: "Parent1-child2-grandchild3", children: [] }] }, { name: "Parent1-child3", children: [] }] }, { name: "Parent2", children: [{ name: "Parent2-child1", children: [] }, { name: "Parent2-child2", children: [{ name: "Parent2-child2-grandchild1", children: [] }, { name: "Parent2-child2-grandchild2", children: [{ name: "Parent2-child2-grandchild2-last", children: [] }] }] }] }, { name: "Parent3", children: [] }] }],
result = filter(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Approach for removing all nested empty children (except the last one. This has an empty object, but no children property).
function filter(array) {
return array.reduce((r, o) => {
if (o.children) {
var children = filter(o.children);
if (children.length) r.push(Object.assign({}, o, { children }));
} else {
r.push(o);
}
return r;
}, []);
}
var data = [{ name: "root", children: [{ name: "Parent1", children: [{ name: "Parent1-child1", children: [{ name: "Parent1-child1-grandchild1", children: [{ name: "Parent1-child1-grandchild1-last", children: [] }] }, { name: "Parent1-child1-grandchild2", children: [] }, { name: "Parent1-child1-grandchild3", children: [] }] }, { name: "Paren1-child2", children: [{ name: "Parent1-chil2-grandchild1", children: [] }, { name: "Parent1-child2-grandchild2", children: [{ name: "Parent1-child2-grandchild2-last", children: [] }] }, { name: "Parent1-child2-grandchild3", children: [] }] }, { name: "Parent1-child3", children: [] }] }, { name: "Parent2", children: [{ name: "Parent2-child1", children: [] }, { name: "Parent2-child2", children: [{ name: "Parent2-child2-grandchild1", children: [] }, { name: "Parent2-child2-grandchild2", children: [{ name: "Parent2-child2-grandchild2-last", children: [] }] }] }] }, { name: "Parent3", children: [{}] }] }],
result = filter(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var data = [{
name: "root",
children: [{
name: "Parent1",
children: [{
name: "Parent1-child1",
children: [{
name: "Parent1-child1-grandchild1",
children: [{
name: "Parent1-child1-grandchild1-last",
children: []
}]
},
{
name: "Parent1-child1-grandchild2",
children: []
},
{
name: "Parent1-child1-grandchild3",
children: []
}
]
},
{
name: "Paren1-child2",
children: [{
name: "Parent1-chil2-grandchild1",
children: []
},
{
name: "Parent1-child2-grandchild2",
children: [{
name: "Parent1-child2-grandchild2-last",
children: []
}]
},
{
name: "Parent1-child2-grandchild3",
children: []
}
]
},
{
name: "Parent1-child3",
children: []
}
]
},
{
name: "Parent2",
children: [{
name: "Parent2-child1",
children: []
},
{
name: "Parent2-child2",
children: [{
name: "Parent2-child2-grandchild1",
children: []
},
{
name: "Parent2-child2-grandchild2",
children: [{
name: "Parent2-child2-grandchild2-last",
children: []
}]
}
]
}
]
},
{
name: "Parent3",
children: []
}
]
}];
function checkChildrens(arr) {
let res = []
arr.forEach(v => {
if (v.children && v.children.length) {
res = res.concat({
name: v.name,
children: checkChildrens(v.children)
})
}
})
return res
}
console.log(checkChildrens(data));

Efficiently loop through an object based on an array of ids

I am getting the users navigation "state" in a mobile navigation as an Array. For example:
['3124', '5312', '5232']
I need to use that state, to grab the Object that has the ID of '5232', 3 levels down in the object.
The Array length can differ, meaning it can return between 1 and 5 ids, so I don't always have to loop all the way down.
This is what the data for the navigation can look like, using the same IDs as I used in the example above, I would like my function to return the "evening" object with ID '5232':
[
{
id: "3124",
name: "women",
children: [
{
id: "5312",
name: "dresses",
children: [
{
id: "8399",
name: "wedding",
children: []
},
{
id: "5232",
name: "evening",
children: []
}
]
},
{
id: "3291",
name: "shoes",
children: []
}
]
},
{
id: "9482",
name: "men",
children: [
{
id: "8292",
name: "jackets",
children: []
},
{
id: "3829",
name: "hats",
children: []
}
]
}
]
I've been talking this through with a couple of colleagues and we can't really figure out a good way to do this efficiently. We cannot change the data, but we can probably change how the user state is saved, if that is wrong.
I could really use some input and ideas on how to solve this problem in a good way.
Simple function to find node by path in tree
function findNodeByPath(nodes, path) {
let node;
if (!path) return;
for (let id of path) {
if (!nodes) break;
for (let child of nodes) {
if (child.id === id) {
node = child;
nodes = node.children;
break;
}
}
}
return node;
}
let nodes = [
{
id: "3124",
name: "women",
children: [
{
id: "5312",
name: "dresses",
children: [
{
id: "8399",
name: "wedding",
children: []
},
{
id: "5232",
name: "evening",
children: []
}
]
},
{
id: "3291",
name: "shoes",
children: []
}
]
},
{
id: "9482",
name: "men",
children: [
{
id: "8292",
name: "jackets",
children: []
},
{
id: "3829",
name: "hats",
children: []
}
]
}
];
console.log(findNodeByPath(nodes, ['3124', '5312', '5232']));
You could iterate the arrays and take only the node of the given path id.
function getObject(tree, path) {
var temp = { children: tree };
return path.every(p => temp = temp.children.find(({ id }) => p === id))
? temp
: undefined;
}
var data = [{ id: "3124", name: "women", children: [{ id: "5312", name: "dresses", children: [{ id: "8399", name: "wedding", children: [] }, { id: "5232", name: "evening", children: [] }] }, { id: "3291", name: "shoes", children: [] }] }, { id: "9482", name: "men", children: [{ id: "8292", name: "jackets", children: [] }, { id: "3829", name: "hats", children: [] }] }],
path = ['3124', '5312', '5232'],
result = getObject(data, path);
console.log(result);
You can try implementing a basic nested for loop if focusing solely on efficiency. I chose the variable name breadcrumb since the concept seems similar to its role in site navigation.
function getState(breadcrumb, state) {
let states = state;
for (let id of breadcrumb) {
for (state of states) {
// found -- continue to next level
if (state.id === id) {
states = state.children;
break;
}
}
// not found
if (state.id !== id) {
return null;
}
}
return state;
}
let state = [{ id: "3124", name: "women", children: [{ id: "5312", name: "dresses", children: [{ id: "8399", name: "wedding", children: [] }, { id: "5232", name: "evening", children: [] }] }, { id: "3291", name: "shoes", children: [] }] }, { id: "9482", name: "men", children: [{ id: "8292", name: "jackets", children: [] }, { id: "3829", name: "hats", children: [] }] }];
let breadcrumb = ['3124', '5312', '5232'];
console.log(getState(breadcrumb, state));
However, if you care more about code maintainability, I recommend a more canonical approach:
function getState(breadcrumb, state) {
return breadcrumb.reduce((state, id) => {
return state !== null
? state.children.find(state => state.id === id)
: state;
}, { children: state });
}
let state = [{ id: "3124", name: "women", children: [{ id: "5312", name: "dresses", children: [{ id: "8399", name: "wedding", children: [] }, { id: "5232", name: "evening", children: [] }] }, { id: "3291", name: "shoes", children: [] }] }, { id: "9482", name: "men", children: [{ id: "8292", name: "jackets", children: [] }, { id: "3829", name: "hats", children: [] }] }];
let breadcrumb = ['3124', '5312', '5232'];
console.log(getState(breadcrumb, state));
try using recursive function
function findById(id) {
var founded = {};
function recurse(data){
for(var i = 0; i < data.length; i++) {
if (data[i].id === id) {
founded = data[i];
} else if (data[i].children && data[i].children.length) {
recurse(data[i].children);
}
}
}
recurse(catalog);
return founded;
};
some demo : Demo

Categories