Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a nested list from a object

Tags:

javascript

I have the following object

var json = {
    items:[
        {id:45 , lable:"Test 1.2.1", parent_id: 2},
        {id:12, lable:"Test 1.0", parent_id: 0},
        {id:32, lable:"Test 1.1", parent_id: 12},
        {id:2, lable:"Test 1.2", parent_id: 12}
    ]
}

Using this object i needed to create a nested list, which will look like the following in the browser

  • Test 1.0
    • Test 1.1
    • Test 1.2
      • Test 1.2.1

My approach was to write a function that would go through the object and store the values in the array of list of elements

function appendItems(json){
    var list = json.items;
    var listelem = [];

    list.forEach(function(i){
        var listelements = document.createElement('li');
        listelements.setAttribute("id",i.id);
        listelements.setAttribute("data-parent-id", i.parent_id);
        listelements.innerText = i.lable;
        listelem.push(listelements);

    })

    console.log(listelem);    
}

But i am unsure of how to get that nested structure from this point

like image 302
RRP Avatar asked Mar 14 '23 06:03

RRP


1 Answers

I would start by transforming you array into an actual tree. You could do it like this:

var obj = {
    items:[
        {id:45 , lable:"Test 1.2.1", parent_id: 2},
        {id:12, lable:"Test 1.0", parent_id: 0},
        {id:32, lable:"Test 1.1", parent_id: 12},
        {id:2, lable:"Test 1.2", parent_id: 12}
    ]
}

// first we create a dictionary so we can find elements by id easily
var objDict = obj.items.reduce(function(p,c) {
    p[c.id] = c;
    c.children = [];
    return p;
}, {});

// then we build our tree
var tree = obj.items.reduce(function(p,c) {
    // if the parent_id is zero, we have found the root.
    if (!c.parent_id) {
        p = c;
    }
    // otherwise, figure out who the parent is and add this one as a child
    else {
        objDict[c.parent_id].children.push(c);
    }
    return p;
}, {});

console.log(JSON.stringify(tree));

This would give you an object that looks like this:

{
    "id": 12,
    "lable": "Test 1.0",
    "parent_id": 0,
    "children": [
        {
            "id": 32,
            "lable": "Test 1.1",
            "parent_id": 12,
            "children": []
        },
        {
            "id": 2,
            "lable": "Test 1.2",
            "parent_id": 12,
            "children": [
                {
                    "id": 45,
                    "lable": "Test 1.2.1",
                    "parent_id": 2,
                    "children": []
                }
            ]
        }
    ]
}

Which should now be easy for you to process. Start with the root and create a node, then do a depth first search down each child making nested nodes as needed.

Something like this:

processTree(tree, document.getElementById("list"));

function processTree(node, element) {
    var li = document.createElement('li');
    li.innerText = node.lable;
    element.appendChild(li);
    if (node.children.length) {
        var ul = document.createElement('ul');
        li.appendChild(ul);
        // note: you might want / need to actual sort the children first
        // but it's not clear from the OP if sorting by id will always give the right order
        for (var i=0; i < node.children.length; i++) {
           processTree(node.children[i], ul);
        }
    }
}

To give you a working example like this:

var obj = {
  items: [{
    id: 45,
    lable: "Test 1.2.1",
    parent_id: 2
  }, {
    id: 12,
    lable: "Test 1.0",
    parent_id: 0
  }, {
    id: 32,
    lable: "Test 1.1",
    parent_id: 12
  }, {
    id: 2,
    lable: "Test 1.2",
    parent_id: 12
  }]
}

var objDict = obj.items.reduce(function(p, c) {
  p[c.id] = c;
  c.children = [];
  return p;
}, {});

var tree = obj.items.reduce(function(p, c) {
  if (!c.parent_id) {
    p = c;
  } else {
    objDict[c.parent_id].children.push(c);
  }
  return p;
}, {});

processTree(tree, document.getElementById("list"));

function processTree(node, element) {
  var li = document.createElement('li');
  li.innerText = node.lable;
  element.appendChild(li);
  if (node.children.length) {
    var ul = document.createElement('ul');
    li.appendChild(ul);
    for (var i = 0; i < node.children.length; i++) {
      processTree(node.children[i], ul);
    }
  }
}
<ul id="list">
</ul>
like image 103
Matt Burland Avatar answered Mar 25 '23 09:03

Matt Burland