1

I faced with common issue converting simple flat data to hierarchical. I have found multiple topics about that but still can't get how to convert flat data exactly to necessary me hierarchical format

this my json

    [
  {
    "id": 1,
    "name": "Sponsor",
    "description": null,
    "parentId": null
  },
  {
    "id": 2,
    "name": "Class",
    "description": null,
    "parentId": 1
  },
  {
    "id": 3,
    "name": "Study",
    "description": null,
    "parentId": 2
  },
  {
    "id": 4,
    "name": "Site",
    "description": null,
    "parentId": 3
  }
]

and I need to get format like this

  [
    {
      "data":{
        "id": 1,
        "name":"Sponsor",
        "description":null,
        "parentId":"null"
      },
      "children":[
        {
          "data":{
            "id": 2,
            "name":"Class",
            "description":null,
            "parentId":"1"
          },
          "children":[
            {
              "data":{
                "id": 3,
                "name":"Study",
                "description":null,
                "parentId":"2"
              },
             "children": [
                {
                   "data":{ 
                     "id": 4,
                     "name":"Site",
                     "description":null,
                     "parentId":"3"
                   }
                 }
              ]
            }
          ]
        }
      ]
    }
  ]

this is my function

    flatToHierarchy(flat) {

    let roots = [];
    let all = {};

    flat.forEach(function (item) {
      all[item.id] = item
    });

    Object.keys(all).forEach(function (id) {
      let item = all[id];
      if (item.parentId === null) {
        roots.push(item)
      } else if (item.parentId in all) {
        let p = all[item.parentId];
        if (!('Children' in p)) {
          p.children = []
        }
        p.children.push(item)
      }
    });

    console.log(roots);
    return roots
  }

output

    [
  {
    "id": 1,
    "name": "Sponsor",
    "description": null,
    "parentId": null,
    "children": [
      {
        "id": 2,
        "name": "Class",
        "description": "Together",
        "parentId": 1,
        "children": [
          {
            "id": 3,
            "name": "Study",
            "description": "browsing data",
            "parentId": 2,
            "children": [
              {
                "id": 4,
                "name": "Site",
                "description": null,
                "parentId": 3,
                "children": []
              }
            ]
          }
        ]
      }
    ]
  }
  ]

I'm pretty close to desire result. Could somebody to help me fix that ?

Edited

the right answer provided by @Someone3 this is slightly modified code for my needs

    flatToHierarchy (flat) {

    let roots = [];
    let all = {};
    let ids = [];

    flat.forEach(function (item) {
      let itemId = item.id;
      let convertedItem = function (id) {
        let newItem = {};
        newItem['data'] = id;
        return newItem;
      } ;
      all[itemId] = convertedItem(item);
      ids.push(itemId);
    });

    for (let i = 0; i < ids.length; i++) {
      let id = ids[i];
      let convertedItem = all[id];
      let parentId = convertedItem.data.parentId;

      if (parentId === null) {
        roots.push(convertedItem);
      } else if (parentId in all) {
        let p = all[parentId];
        if (!('children' in p)) {
          p.children = []
        }
        p.children.push(convertedItem)
      }
    }
    return roots
  }
4
  • Your desired format is not the same as your json. And on your example format, each object has a different parent so they cannot be in the same children: [] array Commented Apr 7, 2017 at 14:11
  • flat.forEach(parent => parent.children = flat.filter(child => child.parentId === parent.id)) Commented Apr 7, 2017 at 14:23
  • and 'Children' !== 'children' Commented Apr 7, 2017 at 14:24
  • or with linear runtime: var root = flat.reduce(function(mapping, node){ if(!(node.id in mapping)) mapping[node.id] = []; if(!(node.parentId in mapping)) mapping[node.parentId] = []; node.children = mapping[node.id]; mapping[node.parentId].push(node); return mapping; })[null]; Commented Apr 7, 2017 at 14:29

3 Answers 3

2

The code below is full source code for your situation. I modified and added a few lines from your source code. Note that this code assumes that parents are always inserted to this tree before their children do. If this assumption is not always true then your code need to be changed more than this.

let flatData = [
  {
    "id": 1,
    "name": "Sponsor",
    "description": null,
    "parentId": null
  },
  {
    "id": 2,
    "name": "Class",
    "description": null,
    "parentId": 1
  },
  {
    "id": 3,
    "name": "Study",
    "description": null,
    "parentId": 2
  },
  {
    "id": 4,
    "name": "Site",
    "description": null,
    "parentId": 3
  }
];

function convertItem(item) {
  let newItem = {};
  newItem.data = item;  
  return newItem;
}

function flatToHierarchy(flat) {

    let roots = [];
    let all = {};
    let ids = [];

    flat.forEach(function (item) {
      let itemId = item.id;
      let convertedItem = convertItem(item);
      all[itemId] = convertedItem;
      ids.push(itemId);
    });

    // We use ids array instead of object to maintain its previous order.
    for (let i = 0; i < ids.length; i++) {
      let id = ids[i];
      let convertedItem = all[id];
      let parentId = convertedItem.data.parentId;

      if (parentId === null) {
        delete convertedItem.data.parentId;
        delete convertedItem.data.id;
        roots.push(convertedItem);
      } else if (parentId in all) {
        let p = all[parentId];
        if (!('Children' in p)) {
          p.children = []
        }
        delete convertedItem.data.parentId;
        delete convertedItem.data.id;
        p.children.push(convertedItem)
      }
    };

    console.log(roots);
    return roots
  }

  flatToHierarchy(flatData);

We can factor out two deletes before push.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot for your help. I accepted your answer because it helps me a lot, but I little bit modified it to fit my code
0

How about my way?

function flatToHierarchy (flatData) {
  const tree = JSON.parse(JSON.stringify(flatData)) // or using `cloneDeep` of lodash library to not side-effect with flatData

  tree.forEach((item) => {
      item.children = tree.filter((element) => element.parent_id === dept.id)
  });
  const roots = tree.filter((item) => item.parent_id === 0)
  return roots
}

Comments

0
const flatToHierarchy = (inputArr, parent = null) => {

      const result = [];

      for(let i = 0; i<inputArr.length; i++) {
    
        if(inputArr[i].parentId === parent) {
          const dataObj = {
            data : {
                ...inputArr[i],
            }
          }

        const children = flatToHierarchy(inputArr, inputArr[i].id);

          if(children.length > 0) {
            dataObj.children = children;
          }

          result.push(dataObj);
         }
      }

    return result;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.