I have a menu application which provides a number of options available in the menu. The options objects is very simple. It has an optionId and a type describing what type of option it is. In addition the option has a long description but I omitted it to keep things simple. The tricky part of the issue is that options can have nested option info, which provides more detail of the item. Here is a full description of the menu type, i.e. menu and the options. The JSON is processed and stored by optionId as key and Option as value in a MenuResult object.
type MenuInfo = {
menuId: string,
menuType: string,
options: Array<Option>
}
type Option = {
optionId: string,
type: string,
options?: Array<Option>
}
type MenuResult ={
menuId: string,
menuType: string,
options: Map<string, Option>
}
Here is an example of the JSON reflecting the elements of the menu.
{
"menuId": "britanny-tavern-menu",
"menuType": "drinks",
"options": [
{
"optionId": "beverages",
"type": "hot-options-1",
"options": [
{
"optionId": "herbal",
"type": "exotic",
"options": [
{
"optionId": "green-tea",
"type": "medicinal"
},
{
"options": [
{
"optionId": "continental-brand",
"type": "loose-leaves"
},
{
"optionId": "asian-brand",
"type": "tea-bags"
}
]
},
{
"optionId": "darjeeling",
"type": "tea-bags"
}
]
}
]
}
] }
Unfortunately my attempt to store the data did not work. I struggle with recursion. Here is my attempt:
function makeMenuResult(menuJson: string){
const menu = JSON.parse(menuJson);
const menuObj = {} as MenuResult;
menuObj.menuId = menu.menuId;
menuObj.menuType = menu.menuType;
const optionsMap = new Map<string, Option>();
for(const optionItem of menu.options){
const option:Option = {
optionId: optionItem.optionId,
type: optionItem.type
}
optionsMap.set(option.optionId, option);
if(optionItem.options && optionItem.options.length > 0){
for(const addOptionItem of optionItem.options){
const map = additionalOptionInfo(addOptionItem);
optionItem.options = map;
}
}
}
}
function additionalOptionInfo(option: Option){
const optionsMap = new Map<string, Option>();
for(const optionItem of option.options){
optionsMap.set(optionItem.optionId, optionItem);
}
return optionsMap;
}
Please help, I don't know where to add the additional recursive step.
CodePudding user response:
In case you want to flatten your nested options into the Map
of MenuResult.options
(i.e. have all options in a single Map), then you can use a recursive function to flatten the array, then convert it into a Map:
function flattenOptions(options: Array<Option>) {
const result: Array<Option> = [];
for (const option of options) {
// Add the current option
result.push(option);
// Add potentially nested options recursively
result.push(...(flattenOptions(option.options ?? [])));
}
return result;
}
// Convert the top level
function makeMenuResult(menuJson: string){
const menu = JSON.parse(menuJson) as MenuInfo;
// Flatten and convert the options into a Map
const optionsMap = new Map<string, Option>();
for (const option of flattenOptions(menu.options)) {
optionsMap.set(option.optionId, option);
}
const menuObj: MenuResult = {
menuId: menu.menuId,
menuType: menu.menuType,
options: optionsMap,
}
return menuObj;
}
On the other hand, if you rather want to convert the MenuInfo
type (which has potentially nested array of Option
's) into a MenuResult
with also potentially nested options, but stored as Map
's instead of arrays, then you also need an extra type to reflect the converted Option
that uses such Map
type, as already done for MenuInfo
and MenuResult
:
type MenuOption = {
optionId: string;
type: string;
options?: Map<string, MenuOption>;
};
// Adjust MenuResult to use that type:
type MenuResult = {
menuId: string,
menuType: string,
options: Map<string, MenuOption>
};
Then with these adjusted types, you can perform a recursive conversion, e.g.:
function optionsJson2map(options: Array<Option>) {
const optionsMap = new Map<string, MenuOption>();
for (const option of options) {
// Convert Option to MenuOption
const menuOption: MenuOption = {
optionid: option.optionId,
type: option.option.type,
};
// Recursive part
if (option.options) {
menuOption.options = optionsJson2map(option.options);
}
optionsMap.set(option.optionId, menuOption);
}
return optionsMap;
}
// Convert the top level
function makeMenuResult(menuJson: string){
const menu = JSON.parse(menuJson) as MenuInfo;
const menuObj: MenuResult = {
menuId: menu.menuId,
menuType: menu.menuType,
options: optionsJson2map(menu.options),
}
return menuObj;
}