Search code examples
javascriptjavascript-objectshierarchy

Javascript - Convert Nested Object to Array of items


I have a object which looks like this:

[
  {
    "id": 1,
    "name": "Electronics",
    "path": "Electronics",
    "children": [
      {
        "id": 2,
        "name": "Laptops & PC",
        "path": "Electronics > Laptops & PC",
        "children": []
      },
      {
        "id": 7,
        "name": "Phones & Accessories",
        "path": "Electronics > Phones & Accessories",
        "children": [
          {
            "id": 8,
            "name": "Smartphones",
            "path": "Electronics > Phones & Accessories > Smartphones",
            "children": [
              {
                "id": 9,
                "name": "Android",
                "path": "Electronics > Phones & Accessories > Smartphones > Android",
                "children": []
              },
              {
                "id": 10,
                "name": "iOS",
                "path": "Electronics > Phones & Accessories > Smartphones > iOS",
                "children": []
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "id": 11,
    "name": "Software",
    "path": "Software",
    "children": []
  }
]

And I wanted to convert it to something like this:

[
  {header: 'Electronics'},
  {name: 'Laptops & PC', group: 'Electronics', id: 2},
  {name: 'Phones & Accessories', group: 'Electronics', id: 7},
  {name: 'Smartphones', group: 'Phones & Accessories', id: 8},
  {name: 'Android', group: 'Smartphones', id: 9},
  {name: 'iOS', group: 'Smartphones', id: 10},
  {divider: true},
  {name: 'Software', group: 'Software', id: 11}
]

Basically,

  • If a root element has children, it should be converted as header.
  • All the elements should have its parent name as group
  • When finished with one root element, It needs to insert {divider: true} before proceeding to the next. (If it's the last root element, do not insert {divider: true}

While I was able to find lots of Hierarchical to JSON solutions on stackoverflow, I was not able to reverse engineer those solutions.

Can somebody please help me?


Solution

  • You could take an iterative and recursive approach by checking header keeping the corresponding divider and iterate children.

    Then you need to add the standard object for any item.

    The recursive callback uses a closure over the group. if not set, the group is a root item.

    function getParts(array) {
        var result = [];
        array.forEach(function iter(group) {
            return function ({ id, name, children }, i, { length }) {
                if (!group && children.length) {
                    result.push({ header: name });
                }
                if (group || !children.length) {
                    result.push({ name, group: group || name, id });
                }
                children.forEach(iter(name));
                if (!group && i + 1 !== length) {
                    result.push({ divider: true });
                }
            };
        }(''));
        return result;
    }
    
    var data = [{ id: 1, name: "Electronics", path: "Electronics", children: [{ id: 2, name: "Laptops & PC", path: "Electronics > Laptops & PC", children: [] }, { id: 7, name: "Phones & Accessories", path: "Electronics > Phones & Accessories", children: [{ id: 8, name: "Smartphones", path: "Electronics > Phones & Accessories > Smartphones", children: [{ id: 9, name: "Android", path: "Electronics > Phones & Accessories > Smartphones > Android", children: [] }, { id: 10, name: "iOS", path: "Electronics > Phones & Accessories > Smartphones > iOS", children: [] }] }] }] }, { id: 11, name: "Software", path: "Software", children: [] }, { id: 11, name: "Software", path: "Software", children: [] }, { id: 11, name: "Software", path: "Software", children: [] }],
        result = getParts(data);
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }