5

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

4
  • Are you open to look at plugins like jsTree? If not it will need recursion. Commented Nov 4, 2015 at 20:35
  • no plugins trying to achieve this via pure js Commented Nov 4, 2015 at 20:35
  • So no to jquery too, right? Commented Nov 4, 2015 at 20:36
  • @ndd right no jquery Commented Nov 4, 2015 at 20:41

2 Answers 2

9

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>

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

1 Comment

thnxs, i was stuck on determining the child to parent relation or how to achieve this, i noticed that "parent_id" value is equal to its parents "id" value in the object. I didn't even think of transforming the array into a tree
0

Here's a simple solution using recursion. I've used a helper function that returns an array of the children for a given parent_id. I hope you find it helpful.

'use strict';

(function() {

    function childrenOf(targetId, items) {
        var children = [];
        items.forEach(function(item) {
            if (item.parent_id === targetId) {
                children.push(item);
            }
        });
        return children;
    }

    function nestedList(id, items) {
        var result = '';
        var children = childrenOf(id, items);
        if (children.length) {
            result = '<ul>';
            children.forEach(function(child) {
                result += '<li>';
                result += child.label;
                result += nestedList(child.id,items);
                result += '</li>';
            });
            result += '</ul>';
        }
        return result;
    }

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

    var rootId = 0;
    var output = nestedList(rootId, json.items);
    console.log('<html><body>');
    console.log(output);
    console.log('</body></html>');



})();

It produces the following output:

<html>

<body>
    <ul>
        <li>Test 1.0
            <ul>
                <li>Test 1.1</li>
                <li>Test 1.2
                    <ul>
                        <li>Test 1.2.1</li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
</body>

</html>

2 Comments

Every call to childrenOf requires you to loop through the whole list again which isn't very efficient.
You're quite right. It would be more efficient to run through the array first and generate a series of child arrays up front.

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.