I have a complex json file that I have to handle with javascript to make it hierarchical. I am trying to convert an array of objects into a deep nested array. There can be any number of divNames with any number of categories and subcategories
Array of objects:
[{
divName: "ABC",
divId: 123,
catName: "XYZ",
catId: 456,
subCatName: "PQR"
subCatId: 781
},
{
divName: "ABC",
divId: 123,
catName: "YQP",
catId: 281,
subCatName: "FYI"
subCatId: 231
},
{
divName: "ABC",
divId: 123,
catName: "XYZ",
catId: 456,
subCatName: "YAB"
subCatId: 587
}
]
Expected output:
[{divName: "ABC",
divId: 123
categories: [
{
catName: "XYZ".
catId: 456,
subCategories: [
{
subCatName: "PQR",
subCatID: 781
},
{
subCatName: "YAB",
subCatID: 587
}],
{
catName: "YQP"
catId: 281,
subCategories: [
{
subCatName: "FYI"
subCatID: 231
}
]
}]
]
This is what I have so far:
nonCompetitorData.map((data, idx) => {
if(idx === 0) {
downloadData.push({"divisionName": data.divisionName, "divisionId": data.divisionId});
} else {
if (!downloadData[0].categories) {
downloadData[0].categories = [];
downloadData[0].categories.push({
"categoryName": data.categoryName,
"categoryId": data.categoryId
})
} else {
if(downloadData[0].categories) {
if(!downloadData[0].categories.some(c => c.categoryName === data.categoryName)) {
downloadData[0].categories.push({
"categoryName": data.categoryName,
"categoryId": data.categoryId
})
}
}
downloadData[0].categories.forEach((cat, i) => {
if(!cat.subCategories) {
console.log("Categories",downloadData[0].categories[i]);
downloadData[0].categories[i].subCategories = [];
downloadData[0].categories[i].subCategories.push({
"subCategoryName": data.subCategoryName,
"subCategoryId": data.subCategoryId
});
} else {
if(cat.subCategories) {
if(!cat.subCategories.some(c => c.subCategoryName === data.subCategoryName)) {
downloadData[0].categories[i].subCategories.push({
"subCategoryName": data.subCategoryName,
"subCategoryId": data.subCategoryId
})
}
}
}
});
}
}
});
Is there a better way to do this?
You can use Array::reduce
and a map to get already added nodes to the tree. If your div, cat, subcat IDs aren't unique together, just add a prefix d
for divs and c
for cats when accessing the map.
const r = arr.reduce((map => (r, {divId,divName,catId,catName,subCatId,subCatName}) => {
const {categories: cats} = map[divId] ??= r[r.length] = {divName, divId, categories:[]};
(map[catId] ??= cats[cats.length] = {catName, catId, subcategories:[]})
.subcategories.push({subCatName, subCatId});
return r;
})({}), []);
console.log(r);
<script>
const arr = [{
divName: "ABC",
divId: 123,
catName: "XYZ",
catId: 456,
subCatName: "PQR",
subCatId: 781
},
{
divName: "ABC",
divId: 123,
catName: "YQP",
catId: 281,
subCatName: "FYI",
subCatId: 231
},
{
divName: "ABC",
divId: 123,
catName: "XYZ",
catId: 456,
subCatName: "YAB",
subCatId: 587
}
]
</script>