Problem
There’s an array of objects that contains plain strings and might contain nested arrays as well. We want to create a new Array that will contain a node for each item in the array and separate nodes for each array item connected to its parent. Each parent node should have the following structure:
{
id: uuidv4(),
position: { x: 0, y: 0 },
data: { label: <item data goes here> }
}
Each array node with the following schema above, should also have a connection edge item added to the array with the following properties:
{
id: ‘e<array item Id>-<parentId>’,
source: <array item Id>,
target: <parentId>,
}
Example
We have the following array of objects for example:
[
{
"author": "John Doe",
"books": [
{
"title": "Book 1"
},
{
"title": "Book 2",
"chapters": [
{
"title": "No Way Home"
}
]
}
]
}
]
The expected output is:
[
{
"id": "1",
"data": {
"label": {
"author": "John Doe"
}
}
},
{
"id": "2",
"data": {
"label": "books" // key of array
}
},
{
"id": "3",
"data": {
"label": {
"title": "Book 1"
}
}
},
{
"id": "4",
"data": {
"label": {
"title": "Book 2"
}
}
},
{
"id": "5",
"data": {
"label": "chapters" // key of array
}
},
{
"id": "6",
"data": {
"label": {
"title": "No Way Home"
}
}
},
{
"id": "e2-1",
"source": "2",
"target": "1"
},
{
"id": "e3-2",
"source": "3",
"target": "2"
},
{
"id": "e4-2",
"source": "4",
"target": "2"
},
{
"id": "e5-4",
"source": "5",
"target": "4"
},
{
"id": "e6-5",
"source": "6",
"target": "5"
}
]
CodePudding user response:
One has to choose a self recursive approach which in a generic way can process both, array-items and object-entries. Also, while the recursive process takes place, one not only has to create and collect the consecutively/serially numbered (the incremented id value) data nodes, but one in addition needs to keep track of every data node's parent reference in order to finally concatenate the list of edge items (as the OP calls it) to the list of data nodes.
function flattenStructureRecursively(source = [], result = [], nodeInfo = {}) {
let { id = 0, parent = null, edgeItems = [] } = nodeInfo;
if (Array.isArray(source)) {
result.push(
...source.flatMap((item, idx) =>
flattenStructureRecursively(item, [], { id: (id idx), parent, edgeItems })
)
);
} else {
Object
.entries(source)
.forEach(([key, value], idx) => {
const dataNode = {};
result.push(
Object.assign(dataNode, {
id: id,
data: { label: key },
})
);
if (parent !== null) {
const { id: pid } = parent;
edgeItems.push({
id: `e${ id }-${ pid }`,
source: id,
target: pid,
});
}
if (idx === 0) {
// every object's first property is supposed to be a parent reference.
parent = dataNode;
}
if (value && (Array.isArray(value) || (typeof value === 'object'))) {
// every object's iterable property is supposed to be a parent reference.
parent = dataNode;
result.push(
...flattenStructureRecursively(value, [], { id, parent, edgeItems })
);
} else {
// (re)assign the final structure of a non iterable property.
dataNode.data.label = { [key]: value };
}
});
}
if (parent === null) {
// append all additionally collected edge items in the end of all the recursion.
result.push(...edgeItems);
}
return result;
}
console.log(
flattenStructureRecursively([{
author: "John Doe",
books: [{
title: "Book 1",
}, {
title: "Book 2",
chapters: [{
title: "No Way Home",
}],
}],
}])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
