We have to print top-level parents with all direct and indirect children. There can be multiple top-level parents. But each child will have only 1 top-level parent only. The input and output format is given below. The output should be an array of objects where each object should have an id and a list of all children. -1 indicates top-level parent and the child may or may not exist in the input(example: 6 in below input).
INPUT:
[
{
id: 1,
children: [2, 3],
parent: -1,
},
{
id: 2,
children: [4, 5],
parent: 1,
},
{
id: 3,
children: [],
parent: 1,
},
{
id: 4,
children: [],
parent: 2,
},
{
id: 5,
children: [6],
parent: 2,
},
{
id: 7,
children: [8, 9],
parent: -1,
},
{
id: 8,
children: [],
parent: 7,
},
{
id: 9,
children: [],
parent: 7,
},
];
OUTPUT:
[
{
id: 1,
children: [2, 3, 4, 5, 6],
},
{
id: 7,
children: [8, 9],
},
];
The solution I tried:
function formatData(data) {
const map = {};
for (let datum of data) {
let parentId = datum.parent === -1 ? datum.id : datum.parent;
if (parentId in map) {
map[parentId].push(datum.id, ...datum.children);
} else {
map[parentId] = [];
map[parentId].push(...datum.children);
}
}
}
CodePudding user response:
Every item has either a parent of -1
(the root to use) or the ID of a parent that's been iterated over already. Use that to iterate over the items and create a Set of each child ID found for each root element. Because some of the objects don't have a parent
matching the root ID that's needed, cache each object's ID with its root parent when iterated over the first time, so that objects coming later can look it up easily.
const data=[{id:1,children:[2,3],parent:-1},{id:2,children:[4,5],parent:1},{id:3,children:[4,5],parent:1},{id:4,children:[],parent:2},{id:5,children:[6],parent:2},{id:7,children:[8,9],parent:-1},{id:8,children:[],parent:7},{id:9,children:[],parent:7}];
function formatData(data) {
const rootParentsById = {};
const outputById = {};
for (const obj of data) {
if (obj.parent === -1) {
outputById[obj.id] = new Set(obj.children);
} else {
const parentId = rootParentsById[obj.parent] ?? obj.parent;
rootParentsById[obj.id] = parentId;
outputById[parentId].add(obj.id);
for (const child of obj.children) {
outputById[parentId].add(child);
}
}
}
return Object.entries(outputById).map(
([id, childrenIdSet]) => ({
id,
children: [...childrenIdSet]
})
);
}
console.log(formatData(data));
CodePudding user response:
This works for me:
const input = [
{
id: 1,
children: [2, 3],
parent: -1,
},
{
id: 2,
children: [4, 5],
parent: 1,
},
{
id: 3,
children: [],
parent: 1,
},
{
id: 4,
children: [],
parent: 2,
},
{
id: 5,
children: [6],
parent: 2,
},
{
id: 7,
children: [8, 9],
parent: -1,
},
{
id: 8,
children: [],
parent: 7,
},
{
id: 9,
children: [],
parent: 7,
},
];
function formatData(data) {
const tmpTree = [...data];
tmpTree.forEach((d) => {
if (d.parent === -1) return;
const parent = tmpTree.find((p) => p.id === d.parent);
if (parent.childNodes) {
parent.childNodes.push(d);
} else {
parent.childNodes = [d];
}
});
const tree = tmpTree.filter((d) => d.parent === -1);
function getChildren(node) {
const children = [...node.children];
if (node.parent !== -1) {
children.push(node.id);
}
if (node.childNodes) {
children.push(...node.childNodes.flatMap(getChildren));
}
return [...new Set(children)];
}
const final = tree.map((node) => {
const children = getChildren(node);
return {
id: node.id,
children,
};
});
return final;
}
console.log(formatData(input));
It does alter the items in the input, but you can rework it to avoid that if needed.