A great opportunity to learn about reusable modules and mutual recursion. This solution in this answer solves your specific problem without any modification of the modules written in another answer. @ScottSauyet, thank you for the concrete input
example -
// Main.js
import { tree } from './Tree' // <- use modules!
const input =
[ { node: { content: 'abc', wordpress_id: 196, wordpress_parent: 193 } }
, { node: { content: 'def', wordpress_id: 193, wordpress_parent: null } } // <- !
, { node: { content: 'ghi', wordpress_id: 199, wordpress_parent: 193 } }
, { node: { content: 'jkl', wordpress_id: 207, wordpress_parent: null } } // <- !
, { node: { content: 'mno', wordpress_id: 208, wordpress_parent: 207 } }
, { node: { content: 'pqr', wordpress_id: 209, wordpress_parent: 208 } }
, { node: { content: 'stu', wordpress_id: 224, wordpress_parent: 207 } }
]
const result =
tree // <- make a tree
( input // <- array of nodes
, ({ node }) => node.wordpress_parent // <- foreign key
, ({ node }, child) => // <- node reconstructor function
({ node: { ...node, child: child(node.wordpress_id) } }) // <- primary key
)
console.log(JSON.stringify(result, null, 2))
Output -
[
{
"node": {
"content": "def",
"wordpress_id": 193,
"wordpress_parent": null,
"child": [
{
"node": {
"content": "abc",
"wordpress_id": 196,
"wordpress_parent": 193,
"child": []
}
},
{
"node": {
"content": "ghi",
"wordpress_id": 199,
"wordpress_parent": 193,
"child": []
}
}
]
}
},
{
"node": {
"content": "jkl",
"wordpress_id": 207,
"wordpress_parent": null,
"child": [
{
"node": {
"content": "mno",
"wordpress_id": 208,
"wordpress_parent": 207,
"child": [
{
"node": {
"content": "pqr",
"wordpress_id": 209,
"wordpress_parent": 208,
"child": []
}
}
]
}
},
{
"node": {
"content": "stu",
"wordpress_id": 224,
"wordpress_parent": 207,
"child": []
}
}
]
}
}
]
In the input
, I used wordpress_parent = null
to represent a root node. We can use 0
like in your original program, if it is required. tree
accepts a fourth parameter, root
, the node to select as the basis of the tree. The default is null
but we can specify 0
, like -
const input =
[ { node: { content: 'abc', wordpress_id: 196, wordpress_parent: 193 } }
, { node: { content: 'def', wordpress_id: 193, wordpress_parent: 0 } } // <- !
, { node: { content: 'ghi', wordpress_id: 199, wordpress_parent: 193 } }
, { node: { content: 'jkl', wordpress_id: 207, wordpress_parent: 0 } } // <- !
, { node: { content: 'mno', wordpress_id: 208, wordpress_parent: 207 } }
, { node: { content: 'pqr', wordpress_id: 209, wordpress_parent: 208 } }
, { node: { content: 'stu', wordpress_id: 224, wordpress_parent: 207 } }
]
const result =
tree
( input
, ({ node }) => node.wordpress_parent
, ({ node }, child) =>
({ node: { ...node, child: child(node.wordpress_id) } })
, 0 // <- !
)
console.log(JSON.stringify(result, null, 2))
// same output ...
To make this post complete, I will include a copy of the Tree
module -
// Tree.js
import { index } from './Index'
const empty =
{}
function tree (all, indexer, maker, root = null)
{ const cache =
index(all, indexer)
const many = (all = []) =>
all.map(x => one(x))
// zero knowledge of node shape
const one = (single) =>
maker(single, next => many(cache.get(next)))
return many(cache.get(root))
}
export { empty, tree } // <- public interface
And the Index
module dependency -
// Index.js
const empty = _ =>
new Map
const update = (r, k, t) =>
r.set(k, t(r.get(k)))
const append = (r, k, v) =>
update(r, k, (all = []) => [...all, v])
const index = (all = [], indexer) =>
all.reduce
( (r, v) => append(r, indexer(v), v) // zero knowledge of value shape
, empty()
)
export { empty, index, append } // <- public interface
For additional insight, I encourage you to read the original Q&A.