Creating a Navbar Menu from JSON data with Lodash 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: &quot;M:A&quot;,
        PARENT_TAG: &quot;MAIN&quot;,
        TITLE: &quot;A Title&quot;
    },
    {
        ID: 2,
        TAG: &quot;AS1&quot;,
        PARENT_TAG: &quot;M:A&quot;,
        TITLE: &quot;A Subtitle 1&quot;
    },
    {
        ID: 3,
        TAG: &quot;AS2&quot;,
        PARENT_TAG: &quot;M:A&quot;,
        TITLE: &quot;A Subtitle 2&quot;
    },
    {
        ID: 4,
        TAG: &quot;AS3&quot;,
        PARENT_TAG: &quot;M:A&quot;,
        TITLE: &quot;A Subtitle 3&quot;
    },
    {
        ID: 5,
        TAG: &quot;M:B&quot;,
        PARENT_TAG: &quot;MAIN&quot;,
        TITLE: &quot;B Title&quot;
    },
    {
        ID: 6,
        TAG: &quot;BS1&quot;,
        PARENT_TAG: &quot;M:B&quot;,
        TITLE: &quot;B Subtitle 1&quot;
    },
    {
        ID: 7,
        TAG: &quot;BS2&quot;,
        PARENT_TAG: &quot;M:B&quot;,
        TITLE: &quot;B Subtitle 2&quot;
    },
    {
        ID: 8,
        TAG: &quot;M:C&quot;,
        PARENT_TAG: &quot;MAIN&quot;,
        TITLE: &quot;C Title&quot;
    },
    {
        ID: 8,
        TAG: &quot;CS1&quot;,
        PARENT_TAG: &quot;M:C&quot;,
        TITLE: &quot;C Subtitle 1&quot;
    }
]

function getMenu() {
    let grouped = _.groupBy(menuConfig, &quot;PARENT_TAG&quot;);
    let menu = getItems(grouped.MAIN, grouped);
    console.log(JSON.stringify(menu, null, 3));
}

function getItems(items, grouped) {
    let subMenu = [];
    _.forEach(items, (item) =&gt; {
        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 &amp;&amp; subMenu.length) {
            return {
                title: item.TITLE,
                subMenu: subMenu
            }
        }
    } else {
        let newItem = {
            title: item.TITLE
        }
        return newItem;
    }
}

getMenu();


Output needs to be like this;
[
  {
    &quot;title&quot;: &quot;A Title&quot;,
    &quot;subMenu&quot;: [
      {
        &quot;title&quot;: &quot;A Subtitle 1&quot;
      },
      {
        &quot;title&quot;: &quot;A Subtitle 2&quot;
      },
      {
        &quot;title&quot;: &quot;A Subtitle 3&quot;
      }
    ]
  },
  {
    &quot;title&quot;: &quot;B Title&quot;,
    &quot;subMenu&quot;: [
      {
        &quot;title&quot;: &quot;B Subtitle 1&quot;
      },
      {
        &quot;title&quot;: &quot;B Subtitle 2&quot;
      }
    ]
  },
  {
    &quot;title&quot;: &quot;C Title&quot;,
    &quot;subMenu&quot;: [
      {
        &quot;title&quot;: &quot;C Subtitle 1&quot;
      }
    ]
  }
]
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) =&gt; {
    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();