javascriptarraystreehierarchyflat

Can you transform a list to a tree where items in the list have children but no depth or parent?


Data looks as follows:

Input:

const items = [
    {
      id: 1,
      name: 'Item 1',
      children: [ 2, 3 ]
    },
    {
      id: 2,
      name: 'Item 2',
      children: null
    },
    {
      id: 3,
      name: 'Item 3',
      children: null
    },
    {
      id: 4,
      name: 'Item 4',
      children: [ 5 ]
    },
    {
      id: 5,
      name: 'Item 5',
      children: [ 6 ]
    },
    {
      id: 6,
      name: 'Item 6',
      children: null
    },
  }
]

Expected Output:

const tree = [
  {
    id: 1,
    name: 'Item 1',
    children: [ 
      {
        id: 2,
        name: 'Item 2',
        children: null
      },
      {
        id: 3,
        name: 'Item 3',
        children: null
      }, 
    ]
  },
  {
    id: 4,
    name: 'Item 4',
    children: [ 
      {
        id: 5,
        name: 'Item 5',
        children: [
          {
            id: 6,
            name: 'Item 6',
            children: null
          }
        ]
      }
    ]
  }
]

If this is in fact possible, would love to 1) see how it is done and 2) see if there are any libraries that handle this use case.


Solution

  • The resulting structure is more a forest than a tree, as not all nodes are connected and you have multiple "roots".

    You can first key the nodes by their id in a Map, and then iterate all children arrays to replace their contents by the corresponding items found in the Map. At the same time keep track of all the children, so that at the end you can identify which items are not children, and therefore belong in the result array:

    const items = [{id: 1,name: 'Item 1',children: [ 2, 3 ]},{id: 2,name: 'Item 2',children: null},{id: 3,name: 'Item 3',children: null},{id: 4,name: 'Item 4',children: [ 5 ]},{id: 5,name: 'Item 5',children: [ 6 ]},{id: 6,name: 'Item 6',children: null},];
    
    const map = new Map(items.map(item => [item.id, item]));
    const children = new Set;
    for (const item of items) {
        if (!item.children) continue;
        for (const id of item.children) children.add(id);
        item.children = item.children?.map(id => map.get(id));
    }
    const forest = items.filter(({id}) => !children.has(id));
    console.log(forest);