While it might be overkill for this requirement, I have handy a variant of one of my utility functions, setPath
, that is used for adding such a path to an existing array. This is a bit different from the other answers in that it does this in an immutable way, returning a new object that shares as much structure as possible with the original one. I always prefer to work with immutable data.
Using that, we can write a hydrate
function to do this job as the one-liner, const hydrate = (paths) => paths .reduce (setPath, [])
.
This is quite likely overkill here, as there is probably no reason to build your output one immutable level after another. But it is a demonstration of the value of keeping utility functions handy.
The code looks like this:
const call = (fn, ...args) => fn (...args)
const setPath = (xs, [name, ...names]) => call (
(i = ((xs .findIndex (x => x .name == name) + 1) || xs .length + 1) - 1) =>
name == undefined
? [...xs]
: [
...xs .slice (0, i),
{name, children: setPath ((i == xs .length) ? [] : xs [i] .children, names)},
...xs .slice (i + 1)
]
)
const hydrate = (paths) => paths .reduce (setPath, [])
console .log (
hydrate ([['A', 'B', 'C'], ['A', 'B', 'D'], ['L', 'M', 'N']])
)
.as-console-wrapper {max-height: 100% !important; top: 0}
We have a trivial call
helper function that I use here to avoid statements. As much as possible, I prefer to work with expressions, as these avoid temporal notions in code brought on by statements, and they compose much better into larger pieces. We could alternatively do this with default parameters, but they have other problems. A trivial call
function handles this nicely.
In our main function, we destructure apart the first name in the input from the remaining ones. Then we pass to call
a function that calculates the index of the element with our existing name in the input array. If it doesn't exist, the index will be the length of that array. This is perhaps over-tricky. findIndex
returns -1
if no element matches. We add 1
to the result, and then, if it's 0
, we choose one more than the length of the array. Finally we subtract 1
from the result, and now the index will be where we found our target or the length of the array if it wasn't found.
Now, if the path is empty, we return a copy of our array. (I prefer the copy just for consistency, but it would be legitimate to just return it directly.) If it's not empty, we use the index to tear apart our input array, keeping everything before it, building a new item for that index by recursively calling setPath
with the remaining node names, and then keeping everything after that index.
And now, as noted, our hydrate
function is a trivial fold of setPath
starting with an empty array.