Q:

Convert flat data to hierarchical with inner objects javascript

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
  }

A:

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.

