Append array of objects by checking the nesting level: Javascript - javascript

I have an nested array of objects with the following format
const arr1 = [
{
name: "internalcorp.com",
children: [
{
name: "internalcorp.com.child1",
children: [
{
name: "internalcorp.com.grandchild1",
children: []
},
{
name: "internalcorp.com.grandchild2",
children: []
}
]
},
{
name: "internalcorp.com.child2",
children: []
}
]
},
{
name: "internalcorpwebsite.com",
children: [
{
name: "internalcorpwebsite.com.child1",
children: []
}
]
}
];
Need to add an className property to the each array object based on its level. Like for example, parent should have level-0 group and children items should have level-x leaf where x is the level number relative to the main parent.
Output should look like
const result = [
{
name: "internalcorp.com",
className: "level-0 group",
children: [
{
name: "internalcorp.com.child1",
className: "level-1 leaf",
children: [
{
name: "internalcorp.com.grandchild1",
className: "level-2 leaf",
children: []
},
{
name: "internalcorp.com.grandchild2",
className: "level-2 leaf",
children: []
}
]
},
{
name: "internalcorp.com.child2",
className: "level-1 leaf",
children: []
}
]
},
{
name: "internalcorpwebsite.com",
className: "level-0 group",
children: [
{
name: "internalcorpwebsite.com.child1",
className: "level-1 leaf",
children: [],
}
],
}
];
Code that I have tried
const result = arr1.map((item,idx)=> {
if(item.children.length){
return {
...item,
className: `level${idx} leaf`
}
}
})

You can use the power of recursion:
function recursive(item, id) {
let returnedItem = item
if (item.children.length) {
returnedItem = {
...returnedItem,
children: returnedItem.children.map(childItem => recursive(childItem, id + 1))
}
}
if (0 === id) {
return { ...returnedItem, className: `level-0 group` }
}
return { ...returnedItem, className: `level-${id} leaf` }
}
const result = arr1.map(item => recursive(item, 0))

Related

How to group an array of objects based on a dot-separated string of numbers

I have an array of objects as the following
const sample = [
{ id: '1' },
{ id: '1.1' },
{ id: '1.1.1' },
{ id: '1.1.2' },
{ id: '1.2' },
{ id: '1.2.1' },
{ id: '1.2.1.1' },
{ id: '2' },
{ id: '2.1' }
];
I'd like to create a new array to include the children under their parent based on id property as the following
[
{
id: '1',
children: [
{
id: '1.1',
children: [
{ id: '1.1.1' },
{ id: '1.1.2' }
]
},
{
id: '1.2',
children: [
{
id: '1.2.1',
children: [{ id: '1.2.1.1' }]
}
]
}
]
},
{
id: '2',
children: [ { id: '2.1' } ]
}
]
I'm not sure how to do it or from where to start
Use a map to keep track of parents and children, then get the entries that are the roots as your result:
const data = [
{ id: '1' },
{ id: '1.1' },
{ id: '1.1.1' },
{ id: '1.1.2' },
{ id: '1.3' },
{ id: '1.3.1' },
{ id: '1.3.1.1' },
{ id: '2' },
{ id: '2.1' }
];
const map = new Map();
data.forEach(({ id }) => {
// exclude last bit to get parent id
const parent = id.split(".").slice(0, -1).join(".");
// our entry - needs to be like this since
// we want a reference to the same object
const entry = { id, children: [] };
// won't run if this is a root
if (parent)
// add child to parent
map.get(parent).children.push(entry);
// add to map
map.set(id, entry);
});
const result = Array.from(map)
// get roots - keys that only have one part
.filter(([key]) => key.split(".").length === 1)
// map to entry values
.map(([, value]) => value);
console.log(result);
.as-console-wrapper { max-height: 100% !important }

Map through array of arrays, to find id and change object

I have an array of ids ['id1', 'id3'].
I also have an array of items:
[
{
children: [{
children: [{
id: "id1", //This is value I need to find
status: { state: false}, //this is value I need to change
}],
}],
},
{
children: [{
children: [{
id: "id2",
status: { state: false},
}],
}],
},
{
children: [{
children: [{
id: "id3",
status: { state: false},
}],
}],
},
]
My goal is to find every item by id from first array, and change attribute state, then return all items having included those I have changed.
This was my try, but it returns all items again, also Im not sure how to change the attribute.
items.filter(item =>
item.children.map(child =>
child.children.map(object =>
idsArray.map(id => id === object.id)
)))
I think you can use recursive function something like below :
let ids = ["id1", "id2"];
let arrayOfItems = [
{
children: [
{
children: [
{
id: "id1",
status: {
state: false
}
}
]
}
]
},
{
children: [
{
children: [
{
id: "id2",
status: {
state: false
}
}
]
}
]
},
{
children: [
{
children: [
{
id: "id3",
status: {
state: false
}
}
]
}
]
}
];
function changeStatus(arrayOfItems, ids) {
return arrayOfItems.map((e) => {
if (e.id && ids.includes(e.id)) {
return { ...e, status: { state: true } };
} else if (e.children) {
return { ...e, children: changeStatus(e.children, ids) };
} else {
return { ...e };
}
});
}
console.log(changeStatus(arrayOfItems,ids));

How to find a tree inside a tree in typescript

let say i have a tree in javascript
a1
--b
----c1
a2
--b2
--b3
----c2
and if i wanted to find c2, it should return a2->b3->c2
Lets say my json looked like this?
treeFamily = {
name : "Parent",
children: [{
name : "Child1",
children: [{
name : "Grandchild1",
children: []
},{
name : "Grandchild2",
children: []
},{
name : "Grandchild3",
children: []
}]
}, {
name: "Child2",
children: []
}]
};
You could check if the nested children have the wanted key/value. Then take the name and hand over the result to the outer call.
function findPath(array, target) {
var path;
array.some(({ name, children }) => {
var temp;
if (name === target) {
path = [name];
return true;
}
if (temp = findPath(children, target)) {
path = [name, ...temp];
return true;
}
});
return path;
}
var treeFamily = { name: "Parent", children: [{ name: "Child1", children: [{ name: "Grandchild1", children: [] }, { name: "Grandchild2", children: [] }, { name: "Grandchild3", children: [] }] }, { name: "Child2", children: [] }] };
console.log(findPath([treeFamily], 'Grandchild2'));
console.log(findPath([treeFamily], 'foo'));
You can use for...of to search the children by calling the function recursively. If the target is found, the name is returned, and combined with the previous names. If not, the function will return undefined. Alternatively, you can return an empty array.
const findPath = (targetName, { name, children }) => {
if(name === targetName) return [name];
for(const child of children) {
const result = findPath(targetName, child);
if(result) return [name, ...result];
}
// if child not found implicitly return undefined or return [] to get an empty array
};
const treeFamily = { name: "Parent", children: [{ name: "Child1", children: [{ name: "Grandchild1", children: [] }, { name: "Grandchild2", children: [] }, { name: "Grandchild3", children: [] }] }, { name: "Child2", children: [] }] };
console.log(findPath('Child2', treeFamily));
console.log(findPath('Grandchild3', treeFamily));
console.log(findPath('Grandchild400', treeFamily));

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

Find node by id in json tree

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;

Categories