I want to group & re-arrange my flat array into an array of objects where each object contains its direct descendent in its children's properties.
My input Data -:
const data = [
{ lineNo: '1' },
{ lineNo: '1-1' },
{ lineNo: '1-2' },
{ lineNo: '1-3' },
{ lineNo: '1-4' },
{ lineNo: '1-11' },
{ lineNo: '1-2-1' },
{ lineNo: '1-2-3' },
{ lineNo: '1-2-2' },
{ lineNo: '1-2-4' },
{ lineNo: '1-3-1' },
{ lineNo: '1-3-2' },
{ lineNo: '1-3-3' },
{ lineNo: '1-11-1' },
{ lineNo: '1-11-2' },
{ lineNo: '1-11-3' },
{ lineNo: '2' },
{ lineNo: '2-1' },
{ lineNo: '2-2' },
{ lineNo: '2-3' },
{ lineNo: '2-2-1' },
{ lineNo: '2-2-2' },
];
The output I want -:
const newData = [
{
lineNo: '1',
children: [{ lineNo: '1-1' }, { lineNo: '1-2' }, { lineNo: '1-3' }, { lineNo: '1-4' }, { lineNo: '1-11' }],
},
{
lineNo: '1-2',
children: [{ lineNo: '1-2-1' }, { lineNo: '1-2-2' }, { lineNo: '1-2-3' }, { lineNo: '1-2-4' }],
},
{
lineNo: '1-3',
children: [{ lineNo: '1-3-1' }, { lineNo: '1-3-2' }, { lineNo: '1-3-3' }],
},
{
lineNo: '1-11',
children: [{ lineNo: '1-11-1' }, { lineNo: '1-11-2' }, { lineNo: '1-11-3' }],
},
{
lineNo: '2',
children: [{ lineNo: '2-1' }, { lineNo: '2-2' }, { lineNo: '2-3' }],
},
{
lineNo: '2-2',
children: [{ lineNo: '2-2-1' }, { lineNo: '2-2-2' }],
},
];
Note: The generated data must have to follow the numerical order mentioned in the output.
What i have done so far -:
const data = [
{ lineNo: '1' },
{ lineNo: '1-1' },
{ lineNo: '1-2' },
{ lineNo: '1-3' },
{ lineNo: '1-4' },
{ lineNo: '1-11' },
{ lineNo: '1-11-1' },
{ lineNo: '1-11-2' },
{ lineNo: '1-11-3' },
{ lineNo: '1-2-1' },
{ lineNo: '1-2-3' },
{ lineNo: '1-2-2' },
{ lineNo: '1-2-4' },
{ lineNo: '1-2-1-1' },
{ lineNo: '1-2-1-2' },
{ lineNo: '1-2-1-3' },
{ lineNo: '1-3-1' },
{ lineNo: '1-3-2' },
{ lineNo: '1-3-3' },
];
function createTree(data: any) {
const tree: any[] = [];
data.reduce(
(r: any, o: any) => {
o.lineNo
.split('-')
.map((_: any, i: any, a: any) => a.slice(0, i 1).join('-'))
.reduce((q: any, lineNo: any, i: any, { length }: any) => {
let temp = (q.children = q.children || []).find((p: any) => p.lineNo === lineNo);
if (!temp) {
q.children.push((temp = { lineNo }));
}
if (i 1 === length) {
Object.assign(temp, o);
}
return temp;
}, r);
return r;
},
{ children: tree }
);
return tree;
}
const createFlat = (data: any) => {
const flat: any[] = [];
const flatData = (data: any) => {
data.forEach((item: any) => {
if (item.children) {
flat.push(item);
flatData(item.children);
}
});
};
flatData(data);
return JSON.parse(JSON.stringify(flat));
};
const getCleanData = (data: any) => {
const cleanData: any[] = [];
function delChildren(obj: any) {
if (obj.children) {
obj.children.forEach((item: any) => {
if (item.children) {
delete item.children;
}
});
}
return obj;
}
data.forEach(function (item: any) {
cleanData.push(delChildren(item));
});
return cleanData;
};
const treeData = createTree(data);
const flatData = createFlat(treeData);
const cleanData = getCleanData(flatData);
console.log(JSON.stringify(cleanData, null, 2));
I am looking for a bit simple and clean approach.
Thank You!
CodePudding user response:
You could first sort the data using a "natural" sort. JavaScript localeCompare
has an option for that.
Then create a Map
keyed by lineNo
and with as corresponding values the objects with the same lineNo
property and an empty children
array property.
Then iterate the data again to populate those children
arrays.
Extract the Map
values and remove those objects that have an empty children
array, unless they are top-level nodes (a boundary case).
Here is an implementation:
function createTree(data) {
// Apply natural ordering
data = [...data].sort((a, b) =>
a.lineNo.localeCompare(b.lineNo, "en", { numeric: true })
);
// Create key/value pairs for all lineNo in a Map
let map = new Map(data.map(({lineNo}) => [lineNo, { lineNo, children: [] }]));
// Populate the children arrays
for (let {lineNo} of data) {
map.get(lineNo.replace(/-?\d $/, ""))?.children?.push({lineNo});
}
// Exclude the nodes that have no children, except if they are top-level
return [...map.values()].filter(({lineNo, children}) =>
!lineNo.includes("-") || children.length
);
}
// Demo
const data = [{ lineNo: '1-1' },{ lineNo: '1-2' },{ lineNo: '1-3-2' },{ lineNo: '1-11-2' },{ lineNo: '1-3' },{ lineNo: '1-11' },{ lineNo: '1-2-3' },{ lineNo: '1-2-2' },{ lineNo: '1' },{ lineNo: '1-2-1' },{ lineNo: '1-2-4' },{ lineNo: '1-11-1' },{ lineNo: '1-3-1' },{ lineNo: '1-4' },{ lineNo: '1-3-3' },{ lineNo: '2-1' },{ lineNo: '2-2' },{ lineNo: '2-2-1' },{ lineNo: '1-11-3' },{ lineNo: '2' },{ lineNo: '2-3' },{ lineNo: '2-2-2' },];
console.log(createTree(data));