An API returns a nested data structure of arrays and objects. The data comes as a list of tree like objects, each with a possible parent-child relationship. The structure itself is shown by the following example code.
[{
label: "search me",
value: "searchme",
children: [{
label: "search me too",
value: "searchmetoo",
children: [{
label: "No one can get me",
value: "anonymous",
}],
}],
}, {
label: "search me2",
value: "searchme2",
children: [{
label: "search me too2",
value: "searchmetoo2",
children: [{
label: "No one can get me2",
value: "anonymous2",
}],
}],
}]
The above data has to be converted into a (flat) array of objects where each object will represent a former node element but with a unique primary key (id). Also a node's parent-id equals its parent's id (the primary key) except for root nodes that do not have a parent, and therefore the parent-id then should be null.
The target structure of the above provided source data then matches the following code ...
[{
id: 1, // DIAGID
parentId: null, // PARENTID
label: "search me", // DIAGNOSIS
value: "searchme" // DIAGTYPE
}, {
id: 2,
parentId: 1,
label: "search me too",
value: "searchmetoo"
}, {
id: 3,
parentId: 2,
label: "No one can get me",
value: "anonymous"
}, {
id: 4,
parentId: null,
label: "search me2",
value: "searchme2"
}, {
id: 5,
parentId: 4,
label: "search me too2",
value: "searchmetoo2"
}, {
id: 6,
parentId: 5,
label: "No one can get me2",
value: "anonymous2"
}]
You could take a recursive method for Array#flatMap and store parent for the next call.
This approach increments id for all nodes.
const
flatTree = (id => parent => ({ children = [], ...object }) => [
{ id: ++id, ...object, parent },
...children.flatMap(flatTree(id))
])(0),
tree = [{ label: 'search me', value: 'searchme', children: [{ label: 'search me too', value: 'searchmetoo', children: [{ label: 'No one can get me', value: 'anonymous' }] }] }, { label: 'four', searchme: '4four' }],
flat = tree.flatMap(flatTree(null));
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is a recursive function dfs that performs a pre-order traversal through the input tree, and passes along a counter that feeds the id property that will be used in the output. Also the current node's id is passed as parentId to the recursive call:
const dfs = (children, counter={id: 1}, parentId=null) =>
children.flatMap(({children=[], ...node}) => [{
...counter,
parentId,
...node
}].concat(dfs(children, counter, counter.id++)));
const response = [{label: "search me",value: "searchme",children: [{label: "search me too",value: "searchmetoo",children: [{label: "No one can get me",value: "anonymous",}],}],}, {label: "search me2",value: "searchme2",children: [{label: "search me too2",value: "searchmetoo2",children: [{label: "No one can get me2",value: "anonymous2",}],}],}];
const result = dfs(response);
console.log(result);
I was going to say a recursive tree walk is all you need, but you can accomplish the same thing easily with a generator:
function *visitNodes( root, parent = null, id = 0 ) {
const node = {
...root,
id : ++id,
parentId = parent ? parent.id : null
};
delete node.children;
yield node;
for ( const child of root.children ?? [] ) {
yield *visitNodes(child, node, id);
}
}
Having defined the generator, you can either iterate over the nodes:
for (const node of visitNodes( tree ) ) {
// do something useful with node here
}
You can convert it into a list easily, either with the spread operator:
const nodes = [...visitNodes(tree)];
or by using Array.from():
const nodes = Array.from( visitNodes(tree) );