Reputation: 1141
I am trying to generate URLs for pages stored in a MongoDB in node.
Using the following function I want to traverse a javascript object that and display the path to each element.
I am nearly there, but I am stuck - There might even be a better way to do this using using Async (which I must admit, confuses me a bit).
Function: (demo)
function printTree(people, slug) {
for (var p = 0; p < people.length; p++) {
var root = people[p];
slug = slug + root.name + "/";
console.log(slug);
if (root.children.length > 0) {
var childrenCount = root.children.length;
for (var c = 0; c < childrenCount; c++) {
if (root.children[c].children.length > 0) {
printTree(root.children[c].children, slug + root.children[c].name + "/");
}
}
}
}
};
Output:
/michael/
/michael/angela/oscar
/michael/meredith/creed
/michael/meredith/creed/kelly
Expected Output:
/michael/
/michael/angela/
/michael/angela/oscar/
/michael/meredith/
/michael/meredith/creed/
/michael/meredith/kelly/
Object:
[
{
"name": "michael",
...
"children": [
{
"name": "angela",
...
"children": [
{
"name": "oscar",
...
"children": []
}
]
},
{
"name": "meredith",
...
"children": [
{
"name": "creed",
...
"children": []
},
{
"name": "kelly",
...
"children": []
}
]
},
{ ... }
]
}
]
If it helps, the data is stored using nested sets: https://github.com/groupdock/mongoose-nested-set So there might be a better way to do the above work using nested sets (negating the above object).
Upvotes: 2
Views: 3936
Reputation: 2181
Not a big fan of reinventing the wheel, so here is a solution using a object-scan. We use it for many data processing tasks and really like it because it makes things easier to maintain. However there is a learning curve. Anyways, here is how you could solve your question
// const objectScan = require('object-scan');
const scanTree = (tree) => objectScan(['**.children'], {
reverse: false,
breakFn: ({ isMatch, parents, context }) => {
if (!isMatch) {
return
}
context.push(
`/${parents
.filter((p) => 'name' in p)
.map(({ name }) => name)
.reverse()
.join('/')}/`
);
}
})(tree, []);
const tree = [{ id: '52fc69975ba8400021da5c7a', name: 'michael', children: [{ id: '52fc69975ba8400021da5c7d', parentId: '52fc69975ba8400021da5c7a', name: 'angela', children: [{ id: '52fc69975ba8400021da5c83', parentId: '52fc69975ba8400021da5c7d', name: 'oscar', children: [] }] }, { id: '52fc69975ba8400021da5c7b', parentId: '52fc69975ba8400021da5c7a', name: 'meredith', children: [{ id: '52fc69975ba8400021da5c7f', parentId: '52fc69975ba8400021da5c7b', name: 'creed', children: [] }, { id: '52fc69975ba8400021da5c7e', parentId: '52fc69975ba8400021da5c7b', name: 'kelly', children: [] }] }, { id: '52fc69975ba8400021da5c7c', parentId: '52fc69975ba8400021da5c7a', name: 'jim', children: [{ id: '52fc69975ba8400021da5c82', parentId: '52fc69975ba8400021da5c7c', name: 'dwight', children: [] }, { id: '52fc69975ba8400021da5c80', parentId: '52fc69975ba8400021da5c7c', name: 'phyllis', children: [] }, { id: '52fc69975ba8400021da5c81', parentId: '52fc69975ba8400021da5c7c', name: 'stanley', children: [] }] }] }];
scanTree(tree).map((e) => console.log(e));
// => /michael/
// => /michael/angela/
// => /michael/angela/oscar/
// => /michael/meredith/
// => /michael/meredith/creed/
// => /michael/meredith/kelly/
// => /michael/jim/
// => /michael/jim/dwight/
// => /michael/jim/phyllis/
// => /michael/jim/stanley/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>
Disclaimer: I'm the author of object-scan
Upvotes: 1
Reputation: 23502
You could also consider something in ECMA5 like this, in case you have further use of the tree
or want to use some a seperator other than /
. Nothing wrong with @bioball answer, this just gives you some more flexibility if wanted.
function makeTree(people, slug, sep) {
slug = slug || '/';
sep = sep || slug;
return people.reduce(function (tree, person) {
var slugPerson = slug + person.name + sep;
return tree.concat(slugPerson, makeTree(person.children, slugPerson, sep));
}, []);
}
function printTree(tree) {
tree.forEach(function (path) {
console.log(path);
});
}
printTree(makeTree(data));
On jsFiddle
Upvotes: 1
Reputation: 1359
Here you go. You don't need a second for
loop, since your printTree
function is going to loop through everything anyway (demo).
function printTree(people, slug){
slug = slug || '/';
for(var i = 0; i < people.length; i++) {
console.log(slug + people[i].name + '/');
if(people[i].children.length){
printTree(people[i].children, slug + people[i].name + '/')
}
}
}
Upvotes: 6