Problem
I am trying to create a navbar menu from JSON data. Actually I have achieved it but I am looking for feedback not to call getItems
twice? How can I improve my code?
Thank you
var _ = require('lodash');
let menuConfig = [
{
ID: 1,
TAG: "M:A",
PARENT_TAG: "MAIN",
TITLE: "A Title"
},
{
ID: 2,
TAG: "AS1",
PARENT_TAG: "M:A",
TITLE: "A Subtitle 1"
},
{
ID: 3,
TAG: "AS2",
PARENT_TAG: "M:A",
TITLE: "A Subtitle 2"
},
{
ID: 4,
TAG: "AS3",
PARENT_TAG: "M:A",
TITLE: "A Subtitle 3"
},
{
ID: 5,
TAG: "M:B",
PARENT_TAG: "MAIN",
TITLE: "B Title"
},
{
ID: 6,
TAG: "BS1",
PARENT_TAG: "M:B",
TITLE: "B Subtitle 1"
},
{
ID: 7,
TAG: "BS2",
PARENT_TAG: "M:B",
TITLE: "B Subtitle 2"
},
{
ID: 8,
TAG: "M:C",
PARENT_TAG: "MAIN",
TITLE: "C Title"
},
{
ID: 8,
TAG: "CS1",
PARENT_TAG: "M:C",
TITLE: "C Subtitle 1"
}
]
function getMenu() {
let grouped = _.groupBy(menuConfig, "PARENT_TAG");
let menu = getItems(grouped.MAIN, grouped);
console.log(JSON.stringify(menu, null, 3));
}
function getItems(items, grouped) {
let subMenu = [];
_.forEach(items, (item) => {
let newItem = getItem(item, grouped)
if (newItem) {
subMenu.push(newItem);
}
});
return subMenu;
}
function getItem(item, grouped) {
if (grouped[item.TAG]) {
let subMenu = getItems(grouped[item.TAG], grouped);
if (subMenu && subMenu.length) {
return {
title: item.TITLE,
subMenu: subMenu
}
}
} else {
let newItem = {
title: item.TITLE
}
return newItem;
}
}
getMenu();
Output needs to be like this;
[
{
"title": "A Title",
"subMenu": [
{
"title": "A Subtitle 1"
},
{
"title": "A Subtitle 2"
},
{
"title": "A Subtitle 3"
}
]
},
{
"title": "B Title",
"subMenu": [
{
"title": "B Subtitle 1"
},
{
"title": "B Subtitle 2"
}
]
},
{
"title": "C Title",
"subMenu": [
{
"title": "C Subtitle 1"
}
]
}
]
Solution
You can use Array.prototype.reduce to group items by TAG
as the key and using the relationship between PARENT_TAG
and TAG
.
This approach will give the expected result by running the array only once
.
const menuConfig = [{
ID: 1,TAG: "M:A", PARENT_TAG: "MAIN", TITLE: "A Title"
}, {
ID: 2, TAG: "AS1", PARENT_TAG: "M:A", TITLE: "A Subtitle 1"
}, {
ID: 3, TAG: "AS2", PARENT_TAG: "M:A", TITLE: "A Subtitle 2"
}, {
ID: 4, TAG: "AS3", PARENT_TAG: "M:A", TITLE: "A Subtitle 3"
}, {
ID: 5, TAG: "M:B", PARENT_TAG: "MAIN", TITLE: "B Title"
}, {
ID: 6, TAG: "BS1", PARENT_TAG: "M:B", TITLE: "B Subtitle 1"
}, {
ID: 7, TAG: "BS2", PARENT_TAG: "M:B", TITLE: "B Subtitle 2"
}, {
ID: 8, TAG: "M:C", PARENT_TAG: "MAIN", TITLE: "C Title"
}, {
ID: 8, TAG: "CS1", PARENT_TAG: "M:C", TITLE: "C Subtitle 1"
}]
function getMenu() {
const menu = getItems(menuConfig, 'MAIN');
console.log(menu);
}
function getItems(items, grandParentTag) {
const newItems = items.reduce((modifiedObj, currentItem) => {
const parentTag = currentItem.PARENT_TAG;
const tag = currentItem.TAG;
if (!modifiedObj[grandParentTag]) {
modifiedObj[parentTag] = {};
}
if (!modifiedObj[grandParentTag][parentTag]) {
modifiedObj[parentTag][tag] = {
title: currentItem.TITLE,
subMenu: [],
};
} else {
modifiedObj[grandParentTag][parentTag].subMenu.push({
title: currentItem.TITLE,
});
}
return modifiedObj;
}, {});
return Object.values(newItems[grandParentTag]);
}
getMenu();