I have an object which I'm trying to filter out elements with a path and map, but I can't get past the first level into the nested children.
My object (with UI components removed):
const items = [
{
path: "/login"
},
{
path: "/help"
},
{
name: "Guidelines",
children: [
{
name: "Section 1",
children: [
{
name: "Chapter 1",
path: "/section-1/chapter-1"
},
{
name: "Chapter 2",
path: "/section-1/chapter-2"
}
]
},
{
name: "Section 2",
children: [
{
name: "Chapter 3",
path: "/section-2/chapter-3"
},
{
name: "Chapter 4",
path: "/section-2/chapter-4"
}
]
}
]
}
];
This filters the elements with a path, but only to the first level:
const filteredRoutes = items.filter((route) => route.path);
Result:
[
{"path":"/login"},
{"path":"/help"}
]
My goal is to have a list of routes with 6 items in this Codesandbox
[
{ "path": "/login" },
{ "path": "/help" },
{ "path": "/section-1/chapter-1" },
{ "path": "/section-1/chapter-2" },
{ "path": "/section-2/chapter-3" },
{ "path": "/section-2/chapter-4" },
]
Thanks
const getPath = (x) => (x.path ? { path: x.path } : x.children?.map(getPath));
const filteredRoutes = items && items.map(getPath).flat(Infinity);
Does this solve your problem?
const filteredRoutes = [];
const arr = items.map((item) => {
if (item.path) {
filteredRoutes.push({"path" : item.path});
} else {
item.children.map((child) => {
if (child.children) {
child.children.map((_child) => {
filteredRoutes.push({"path" : _child.path});
})
}
})
}
});
console.log(filteredRoutes);
Would something like this work?
const findRoutesWithPaths = (routes) => {
if (!routes) {
return [];
}
const filteredRoutes = [];
// Loop over all the routes
routes.forEach((item) => {
// Add `path` from self
if (item.path) {
filteredRoutes.push(item);
}
// Add `path`s from children
if (item.children) {
filteredRoutes.push(...findRoutesWithPaths(item.children));
}
});
return filteredRoutes;
};
const filteredRoutes = findRoutesWithPaths(items);
codesandbox
Although you wanted to use the filter method, I found a way to iterate your array of objects recursively in case you have an unknown depth, your pathArray should have a length of 6 given the example data, but it will work of you have more children in your data as well.
var pathArray = [];
//Loop through all the objects in your items array
for (var k = 0; k < items.length; k++) {
//For each object let's gather all the paths in the object
var route = items[k];
function getPath(obj) {
//If the object has a "children" attribute then we should look inside
if (obj.hasOwnProperty("children")) {
for (var i = 0; i < obj.children.length; i++) {
getPath(obj.children[i]);
}
}
// If not then this is the base level, which means there is a path attribute we need to grab
else {
pathArray.push(obj.path); //Add the path to our array
}
}
getPath(route);
}
Let me know if you need any more clarification
Related
Let's assume I have the following array:
[
"About.vue",
"Categories/Index.vue",
"Categories/Demo.vue",
"Categories/Flavors.vue"
]
We use the Index.vue in each sub-folder to act as the parent of that folder. That means the above would look like:
[
{
name: "About",
children: []
},
{
name: "Categories",
children:
[
{
name: "Index.vue",
children: []
},
{
name: "Demo.vue",
children: []
},
{
name: "Flavors.vue",
children: []
}
]
}
]
I was able to get it working slightly by using the following tutorial: https://joelgriffith.net/array-reduce-is-pretty-neat/
However, the thing about that is that it is a root object with a property for each file, as opposed to an array with an object for each file.
The following code produces the intended output:
let paths = [
"About.vue",
"Categories/Index.vue",
"Categories/Demo.vue",
"Categories/Flavors.vue"
];
let helper = {
index: -1,
name: ""
};
function treeify(files) {
var fileTree = [];
function mergePathsIntoFileTree(prevDir, currDir, i, filePath) {
helper.name = currDir;
helper.index = i;
if (helper.index == 0) {
let index = prevDir.findIndex(x => x.name == helper.name);
if (index < 0) {
prevDir.push({
name: helper.name,
children: []
});
}
return prevDir;
}
if (helper.index >= 0) {
let obj = {
name: currDir,
children: []
};
prevDir[helper.index].children.push(obj);
helper.index = i;
helper.name = currDir;
}
}
function parseFilePath(filePath) {
var fileLocation = filePath.split('/');
// If file is in root directory, eg 'index.js'
if (fileLocation.length === 1) {
fileTree[0] = {
name: fileLocation[0],
children: []
};
} else {
fileLocation.reduce(mergePathsIntoFileTree, fileTree);
}
}
files.forEach(parseFilePath);
return fileTree;
}
console.log(treeify(paths));
However, it fails on the following input:
let paths = [
"About.vue",
"Categories/Index.vue",
"Categories/Demo.vue",
"Categories/Flavors.vue",
"Categories/Types/Index.vue",
"Categories/Types/Other.vue"
];
Does anyone know a solution to get it working for further nested lists of paths?
You can create this structure using forEach method to loop each path and split it to array on /, then you can also use reduce method to create nested objects.
let paths = ["About.vue","Categories/Index.vue","Categories/Demo.vue","Categories/Flavors.vue","Categories/Types/Index.vue","Categories/Types/Other.vue"];
let result = [];
let level = {result};
paths.forEach(path => {
path.split('/').reduce((r, name, i, a) => {
if(!r[name]) {
r[name] = {result: []};
r.result.push({name, children: r[name].result})
}
return r[name];
}, level)
})
console.log(result)
So, first off, I am going to assume this is in Node.js, second, I am currently at home so I don't have access to node.js at the moment so I had no real way of testing the code, however the following code should work.
What you need to do is check the contents of the folder and then make a check to see if an item in the folder is a directory or not, if true, call the function again with the new path (a.k.a. recursion).
So first you start by reading the folder, add each item's name to the .name property of the object, then you check if it's a folder or not, if it is, recursive for that path. Keep returning an array of objects back (this will be added to the .children property.
var fs = require('fs');
var filetree = DirToObjectArray('path/to/folder/');
function DirToObjectArray(path) {
var arr = [];
var content = fs.readdirSync(path, { withFileTypes: true });
for (var i=0; i< content.length; i++) {
var obj = new Object({
name: "",
children: []
});
obj.name = content[i].name;
if (content[i].isDirectory()) {
obj.children = DirToObjectArray(path + content[i].name + "/");
}
arr.push(obj);
}
return arr;
}
If you are not using node.js but in-browser javascript, I can't help you with that
You could take an iterative approach for every found name part and get an object and return the children for the next search.
var paths = ["About.vue", "Categories/Index.vue", "Categories/Demo.vue", "Categories/Flavors.vue", "Categories/Types/Index.vue", "Categories/Types/Other.vue"],
result = paths.reduce((r, p) => {
var names = p.split('/');
names.reduce((q, name) => {
var temp = q.find(o => o.name === name);
if (!temp) q.push(temp = { name, children: [] });
return temp.children;
}, r);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I went with #Nenad Vracar's answer (and upvoted, thank you!), but I also had the need to allow duplicate filenames in my use case. I just wanted to share how I did that.
let paths = ["About.vue","Categories/Index.vue","Categories/Demo.vue","Categories/Flavors.vue","Categories/Types/Index.vue","Categories/Types/Other.vue","Categories/Types/Other.vue","Categories/Types/Other.vue"];
let result = [];
let level = {result};
paths.forEach(path => {
path.split('/').reduce((r, name, i, a) => {
if(!r[name]) {
r[name] = {result: []};
r.result.push({name, children: r[name].result});
} else if (i === a.length - 1) {
// Allow duplicate filenames.
// Filenames should always be at the end of the array.
r.result.push({name, children: []});
}
return r[name];
}, level)
})
console.log(result)
The following solution was derived from #nenad-vracar's answer. One shortcoming with his answer is that if a path contains "result", the code will fail. A simple workaround would be to rename "result" to "", that is, include characters that cannot appear in a path.
export interface IPathNode {
name: string;
children: IPathNode[];
path: IPath | null;
}
export interface IPath {
key: string;
directory: boolean;
}
interface IPathLevel {
// ["<result>"]: IPathNode[];
[key: string]: IPathLevel | IPathNode[];
}
export const createPathTree = (paths: IPath[]): IPathNode | null => {
const level: IPathLevel = { ["<result>"]: [] as IPathNode[] };
paths.forEach((path) => {
path.key.split("/").reduce(
((
currentLevel: IPathLevel,
name: string,
index: number,
array: string[]
) => {
if (!currentLevel[name]) {
currentLevel[name] = { ["<result>"]: [] };
(currentLevel["<result>"] as IPathNode[]).push({
name,
children: (currentLevel[name] as IPathLevel)[
"<result>"
] as IPathNode[],
/* Attach the path object to the leaf node. */
path: index === array.length - 1 ? path : null,
});
}
return currentLevel[name];
}) as any,
level
);
});
const finalArray = level["<result>"] as IPathNode[];
return finalArray.length > 0 ? finalArray[0] : null;
};
console.log(
JSON.stringify(
createPathTree([
{
key: "/components/button.tsx",
directory: false,
},
{
key: "/components/checkbox.tsx",
directory: false,
},
{
key: "/result",
directory: true,
},
]),
null,
4
)
);
Output:
{
"name": "",
"children": [
{
"name": "components",
"children": [
{
"name": "button.tsx",
"children": [],
"path": {
"key": "/components/button.tsx",
"directory": false
}
},
{
"name": "checkbox.tsx",
"children": [],
"path": {
"key": "/components/checkbox.tsx",
"directory": false
}
}
],
"path": null
},
{
"name": "result",
"children": [],
"path": {
"key": "/result",
"directory": true
}
}
],
"path": null
}
Tying to change key values inside an object but its adding double values or its adding all values at once. Every name must get an value which in this case is a language slug. Not sure what I am doing wrong.
// the data
const routesObj = [
{ name: 'dashboard.index' },
{ name: 'settings.index' },
{ name: 'settings.general' },
{ ... }
]
// end results (how i want it to be)
[
{
nl: {
routes: [
{
name: 'nl.dashboard.index'
},
{
name: 'nl.dashboard.index'
},
{
name: 'nl.settings.general'
}
]
}
},
{
en: {
routes: [
{
name: 'en.dashboard.index'
},
{
name: 'en.dashboard.index'
},
{
name: 'en.settings.general'
}
]
}
}
]
// how its working now(not good)
[
{
nl: {
routes: [
{
name: 'en.nl.dashboard.index'//adding both languages
},
...
]
}
},
...
]
const routeBuilder = (routes, languages) => {
let newRoutes = []
languages.forEach(function(lang){
Object.keys(routes).forEach(function(key){
routes[key]['name'] = lang+'.'+routes[key]['name']
});
newRoutes[lang] = {routes};
});
return newRoutes
}
routeBuilder(routesObj, ['nl','en'])
The problem is here:
Object.keys(routes).forEach(function(key){
routes[key]['name'] = lang+'.'+routes[key]['name']
});
newRoutes[lang] = {routes};
routes is a reference to routesObj, so the code above modifies the original object in each languages.forEach iteration. The solution is to clone routes so that each iteration has a unique copy.
newRoutes is an array, but it's being used like an object in newRoutes[lang]. To insert an object into the newRoutes array, use Array.prototype.push.
const routesObj = [
{ name: 'dashboard.index' },
{ name: 'settings.index' },
{ name: 'settings.general' },
]
const routeBuilder = (routes, languages) => {
const newRoutes = []
languages.forEach(function(lang) {
routes = routesObj.map(x => ({...x})); // 1️⃣
Object.keys(routes).forEach(function(key){
routes[key]['name'] = lang+'.'+routes[key]['name']
});
newRoutes.push({ [lang]: { routes } }); // 2️⃣
})
return newRoutes;
}
const routes = routeBuilder(routesObj, ['nl','en'])
console.log(routes)
Alternatively, use nested Array.prototoype.maps:
const routesObj = [
{ name: 'dashboard.index' },
{ name: 'settings.index' },
{ name: 'settings.general' },
]
const routeBuilder = (routes, languages) => {
return languages.map(lang => {
return {
[lang]: {
routes: routes.map(route => ({ ...route, name: lang + '.' + route.name })),
}
}
})
}
const routes = routeBuilder(routesObj, ['nl','en'])
console.log(routes)
I am working on a solution where I have a deep array of parent having child elements
Here is how the array look like
[
{
"id": "1",
"Name": "John Doe",
"children":
[
{
"id": "1.1",
"name": "John doe 1.1"
},
{
"id:": "1.2",
"name:": "John doe 1.2"
},
{
"id": "1.3",
"name": "John doe 1.3",
"children":
[
{
"id": "1.3.1",
"name": "John doe 1.3.1"
}
]
}
]
},
{
"id": "2",
"Name": "Apple",
"children":
[
{
"id": "2.1",
"name": "Apple 2.1"
},
{
"id:": "1.2",
"name:": "Apple 1.2"
}
]
}
]
basically, I have a functionality where I have a table whenever the user clicks on a row I want to add children related to that row,
For example, whenever I click on the row with id 1, I call click function by passing row as an argument, find an index for row and append children under that along with maintaining state, my solution works only for one level nested child, suppose if I want to add children property under children it's not working
Here is the function that I wrote
const expandRow = (row) => {
const index = _(this.state.data)
.thru(function(coll) {
return _.union(coll, _.map(coll, 'children') || []);
})
.flattenDeep()
.findIndex({ id: row.id });
console.log(index)
if (index !== -1) {
let prevState = [...this.state.data];
let el = _(prevState)
.thru(function(coll) {
return _.union(coll, _.map(coll, 'children') || []);
})
.flattenDeep()
.find({ id: row.id });
console.log(el)
el.children = [
{ id: '_' + Math.random().toString(36).substr(2, 5), name: "sfsdfds1", isExpanded:false,parentId:row.id },
{ id: '_' + Math.random().toString(36).substr(2, 5), name: "sfsdfds2",isExpanded:false,parentId:row.id },
];
this.setState({data:[...this.state.data],prevState},()=>{console.log(this.state.data)})
}
updateState(row.id, { isExpanded: true });
};
I also want to maintain state along with it so whenever the user adds a new row my component re-render.
You need recursive function for this.below is the code I write in VueJs for parent child deep array. please take a look hope it's provide you some idea.
and one more thing my data structure is same as your.
let treeData= {
id:1,
type: 0,
status: 0,
parent_id:0,
children: [{
id:1,
type: 0,
status: 0,
parent_id:1,
children:[
{
id:1,
type: 0,
status: 0,
parent_id:1,
}
]
}],
}
ChangeCheckStatus(treedata, item, status) {
for (let i = 0; i < treedata.length; i++) {
if (treedata[i].id === item.id) {
treedata[i].selectAll = status;
return;
}
this.ChangeCheckStatus(treedata[i].children, item, status);
}
}
makeTreeViewThroughCsvData(csvData) {
const data = this.csvToJSON(csvData)
this.rows_new = [...this.rows_new, ...data];
this.rows_new.forEach((_data) => {
let newNode = {}
for (const key in _data) {
if (_data.hasOwnProperty(key)) {
newNode[key.trim()] = _data[key]
}
}
newNode['children'] = []
newNode['status'] = _data.status
/* eslint-disable */
newNode = rest
//variable hold new tree data
this.treeData.push(newNode)
})
this.generateFinalTreeData();
},
generateFinalTreeData() {
const root = []
const nodeIds = []
const mapping = {}
this.treeData.forEach(node => {
// No parentId means Node
if (node.parent_id != undefined) {
//increment NODE ID only when parent_is is not undefined
nodeIds.push(node.id)
}
if (node.parent_id == 0 || node.parent_id == 1) return root.push(node);
// Insert node as child of parent
let parentKey = mapping[node.parent_id];
if (typeof parentKey !== "number") {
parentKey = this.treeData.findIndex(el => el.id === node.parent_id);
mapping[node.parent_id] = parentKey;
}
if (!this.treeData[parentKey].children) {
return this.treeData[parentKey].children = [node];
}
this.treeData[parentKey].children.push(node);
});
this.finalTreeData = root
//vuex commit statement == Redux dispach
this.$store.commit('setTreeViewData', root);
this.$store.commit('setMaxNodeId', Math.max(...nodeIds) + 1);
}
How do I correctly build a nested list of categories so I could use it in frontend with <select> box element ?
Collection Type "Categories" have following fields:
categories.id
categories.name
categories.category
the handler is in /api/categories/controllers/categories.js:
async nested(ctx) {
let entities = await strapi.services.categories.find(ctx.query);
const cleaned = entities.map(entity => {
const item = sanitizeEntity(entity, { model: strapi.models.categories });
return item;
})
return nestChilds(cleaned);
}
function nestChilds (object) {
const list = [];
object.forEach(i => {
console.log('LIST:', list);
if (!i.parent) {
list.push(i);
} else {
const parent = list.find(x => x.id === i.parent.id);
parent.childs = [];
parent.childs.push(i);
}
})
return list;
}
But it's not working. I would like to have some sort of these results:
[
{
id: 1,
name: "Top-level category",
childs: [
{
id: 2,
name: "2nd level category 1"
},
{
id: 3,
name: "2nd level category 2",
childs: [
{
id: 5,
name: "3rd level category 1"
}
]
},
{
id: 4,
name: "2nd level category 3"
}
]
}
]
Is there any solution or maybe someone could kickstart me with an idea ?
So I came up with the following solution:
function nestChilds (object) {
const list = [];
object.forEach(i => {
// If is related to parent
if (i.parent) {
// Scope that parent
const parent = object.find(({ id }) => id === i.parent.id);
// Add a child array property to that parent if not already done
if (!parent.hasOwnProperty('childs')) parent.childs = [];
// Add current item to it's corresponding parent
parent.childs.push(i);
// Remove parent property
delete i.parent;
} else {
list.push(i);
}
})
return list;
}
Let's assume I have the following array:
[
"About.vue",
"Categories/Index.vue",
"Categories/Demo.vue",
"Categories/Flavors.vue"
]
We use the Index.vue in each sub-folder to act as the parent of that folder. That means the above would look like:
[
{
name: "About",
children: []
},
{
name: "Categories",
children:
[
{
name: "Index.vue",
children: []
},
{
name: "Demo.vue",
children: []
},
{
name: "Flavors.vue",
children: []
}
]
}
]
I was able to get it working slightly by using the following tutorial: https://joelgriffith.net/array-reduce-is-pretty-neat/
However, the thing about that is that it is a root object with a property for each file, as opposed to an array with an object for each file.
The following code produces the intended output:
let paths = [
"About.vue",
"Categories/Index.vue",
"Categories/Demo.vue",
"Categories/Flavors.vue"
];
let helper = {
index: -1,
name: ""
};
function treeify(files) {
var fileTree = [];
function mergePathsIntoFileTree(prevDir, currDir, i, filePath) {
helper.name = currDir;
helper.index = i;
if (helper.index == 0) {
let index = prevDir.findIndex(x => x.name == helper.name);
if (index < 0) {
prevDir.push({
name: helper.name,
children: []
});
}
return prevDir;
}
if (helper.index >= 0) {
let obj = {
name: currDir,
children: []
};
prevDir[helper.index].children.push(obj);
helper.index = i;
helper.name = currDir;
}
}
function parseFilePath(filePath) {
var fileLocation = filePath.split('/');
// If file is in root directory, eg 'index.js'
if (fileLocation.length === 1) {
fileTree[0] = {
name: fileLocation[0],
children: []
};
} else {
fileLocation.reduce(mergePathsIntoFileTree, fileTree);
}
}
files.forEach(parseFilePath);
return fileTree;
}
console.log(treeify(paths));
However, it fails on the following input:
let paths = [
"About.vue",
"Categories/Index.vue",
"Categories/Demo.vue",
"Categories/Flavors.vue",
"Categories/Types/Index.vue",
"Categories/Types/Other.vue"
];
Does anyone know a solution to get it working for further nested lists of paths?
You can create this structure using forEach method to loop each path and split it to array on /, then you can also use reduce method to create nested objects.
let paths = ["About.vue","Categories/Index.vue","Categories/Demo.vue","Categories/Flavors.vue","Categories/Types/Index.vue","Categories/Types/Other.vue"];
let result = [];
let level = {result};
paths.forEach(path => {
path.split('/').reduce((r, name, i, a) => {
if(!r[name]) {
r[name] = {result: []};
r.result.push({name, children: r[name].result})
}
return r[name];
}, level)
})
console.log(result)
So, first off, I am going to assume this is in Node.js, second, I am currently at home so I don't have access to node.js at the moment so I had no real way of testing the code, however the following code should work.
What you need to do is check the contents of the folder and then make a check to see if an item in the folder is a directory or not, if true, call the function again with the new path (a.k.a. recursion).
So first you start by reading the folder, add each item's name to the .name property of the object, then you check if it's a folder or not, if it is, recursive for that path. Keep returning an array of objects back (this will be added to the .children property.
var fs = require('fs');
var filetree = DirToObjectArray('path/to/folder/');
function DirToObjectArray(path) {
var arr = [];
var content = fs.readdirSync(path, { withFileTypes: true });
for (var i=0; i< content.length; i++) {
var obj = new Object({
name: "",
children: []
});
obj.name = content[i].name;
if (content[i].isDirectory()) {
obj.children = DirToObjectArray(path + content[i].name + "/");
}
arr.push(obj);
}
return arr;
}
If you are not using node.js but in-browser javascript, I can't help you with that
You could take an iterative approach for every found name part and get an object and return the children for the next search.
var paths = ["About.vue", "Categories/Index.vue", "Categories/Demo.vue", "Categories/Flavors.vue", "Categories/Types/Index.vue", "Categories/Types/Other.vue"],
result = paths.reduce((r, p) => {
var names = p.split('/');
names.reduce((q, name) => {
var temp = q.find(o => o.name === name);
if (!temp) q.push(temp = { name, children: [] });
return temp.children;
}, r);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I went with #Nenad Vracar's answer (and upvoted, thank you!), but I also had the need to allow duplicate filenames in my use case. I just wanted to share how I did that.
let paths = ["About.vue","Categories/Index.vue","Categories/Demo.vue","Categories/Flavors.vue","Categories/Types/Index.vue","Categories/Types/Other.vue","Categories/Types/Other.vue","Categories/Types/Other.vue"];
let result = [];
let level = {result};
paths.forEach(path => {
path.split('/').reduce((r, name, i, a) => {
if(!r[name]) {
r[name] = {result: []};
r.result.push({name, children: r[name].result});
} else if (i === a.length - 1) {
// Allow duplicate filenames.
// Filenames should always be at the end of the array.
r.result.push({name, children: []});
}
return r[name];
}, level)
})
console.log(result)
The following solution was derived from #nenad-vracar's answer. One shortcoming with his answer is that if a path contains "result", the code will fail. A simple workaround would be to rename "result" to "", that is, include characters that cannot appear in a path.
export interface IPathNode {
name: string;
children: IPathNode[];
path: IPath | null;
}
export interface IPath {
key: string;
directory: boolean;
}
interface IPathLevel {
// ["<result>"]: IPathNode[];
[key: string]: IPathLevel | IPathNode[];
}
export const createPathTree = (paths: IPath[]): IPathNode | null => {
const level: IPathLevel = { ["<result>"]: [] as IPathNode[] };
paths.forEach((path) => {
path.key.split("/").reduce(
((
currentLevel: IPathLevel,
name: string,
index: number,
array: string[]
) => {
if (!currentLevel[name]) {
currentLevel[name] = { ["<result>"]: [] };
(currentLevel["<result>"] as IPathNode[]).push({
name,
children: (currentLevel[name] as IPathLevel)[
"<result>"
] as IPathNode[],
/* Attach the path object to the leaf node. */
path: index === array.length - 1 ? path : null,
});
}
return currentLevel[name];
}) as any,
level
);
});
const finalArray = level["<result>"] as IPathNode[];
return finalArray.length > 0 ? finalArray[0] : null;
};
console.log(
JSON.stringify(
createPathTree([
{
key: "/components/button.tsx",
directory: false,
},
{
key: "/components/checkbox.tsx",
directory: false,
},
{
key: "/result",
directory: true,
},
]),
null,
4
)
);
Output:
{
"name": "",
"children": [
{
"name": "components",
"children": [
{
"name": "button.tsx",
"children": [],
"path": {
"key": "/components/button.tsx",
"directory": false
}
},
{
"name": "checkbox.tsx",
"children": [],
"path": {
"key": "/components/checkbox.tsx",
"directory": false
}
}
],
"path": null
},
{
"name": "result",
"children": [],
"path": {
"key": "/result",
"directory": true
}
}
],
"path": null
}
My answer is inspired from #Nenad Vracar. But unlike his solution where he used for each and reduce which I think unnecessary.
let final = {result:[]};
for (const path of paths) {
let context = final;
for (const name of path.split('/')) {
if (!context[name]) {
context[name] = {result:[]};
context.result.push({name, children: context[name].result});
}
context = context[name];
}
}
console.log(final.result)