Create Nested Tree View with Only One Query.

I have struggled with some kind of multi level tree view maybe you too, usually the easiest way to make this kind of data structure is using a recursive function which will get the data object and put where condition like WHERE parentId = <ID>.

So let say I have table like this.

|id|parentId|category|
|--|--------|--------|
|1 |  null  |  Car   |
|2 |  null  | Gadget |
|3 |   1    | SUV    |
|4 |   1    |  MVP   |
|5 |   3    |  LSUV  |
|--|--------|--------|

First of all, create query to get all of the rows in database.

SELECT * FROM TABLE_NAME;

We get the result from the query with data structure as follows,

[{ id: 1, parentId: null, category: "Car" }, { id: 2, parentId: null, category: "Gadget" }, { id: 3, parentId: 1, category: "SUV" }, { id: 4, parentId: 1, category: "MVP" }, { id: 5, parentId: 3, category: "LSUV" }]

If you use language such as Javascript or Python or maybe Go, you likely can find the shorthand to structure those data to be tree view easily. There are a lot of quick and simple solution on Stackoverflow.

So I will use Javascript here.

But, I won't give you the shorthand solution. I found this interesting solution from a blog post written in 2007 using PHP.

After we got the array of object SQL query above, then we can create a hashMap or objectMap.

const itemGroup = {
  item: {},
  parents: {}
}

Then we can fill itemGroup with the data we already have from SQL query.

We can have something like this.

const data = <SQL query result>

data.forEach((e) => {
  itemGroup.item[e.id] = e;

  if (itemGroup.parents[e.parentId]) {
    itemGroup.parents[e.parentId].push(e.id)
  } else {
    itemGroup.parents[e.parentId] = [e.id]
  }
})

Not really straightforward 😅

If we console log, we got some data like this.

{
  "item": {
    "1": {
      "id": 1,
      "parentId": null,
      "category": "Car"
    },
    "2": {
      "id": 2,
      "parentId": null,
      "category": "Gadget"
    },
    "3": {
      "id": 3,
      "parentId": 1,
      "category": "SUV"
    },
    "4": {
      "id": 4,
      "parentId": 1,
      "category": "MVP"
    },
    "5": {
      "id": 5,
      "parentId": 3,
      "category": "LSUV"
    }
  },
  "parents": {
    "1": [
      3,
      4
    ],
    "3": [
      5
    ],
    "null": [
      1,
      2
    ]
  }
}

With this data structure, you can create a recursive function to loop over the parent property.

function buildTree (parentId, data) {
  let newData = []

  if (data.parents && data.parents[parentId]) {
    for (itemId of data.parents[parentId]) {
      let newObject = data.item[itemId]

      newObject.child = buildTree(itemId, data)

      newData.push(newObject)
    }
  }

  return newData
}

const result = buildTree(null, itemGroup)

Because of the parentId of the top level item is null, so let's we set null here as the first parameter.

We check if given parentId exists in parents property, then it will loop over the parents[parentId] arrays.

Here is the end result:

[
  {
    "id": 1,
    "parentId": null,
    "category": "Car",
    "child": [
      {
        "id": 3,
        "parentId": 1,
        "category": "SUV",
        "child": [
          {
            "id": 5,
            "parentId": 3,
            "category": "LSUV",
            "child": []
          }
        ]
      },
      {
        "id": 4,
        "parentId": 1,
        "category": "MVP",
        "child": []
      }
    ]
  },
  {
    "id": 2,
    "parentId": null,
    "category": "Gadget",
    "child": []
  }
]

26