Lailoken
Lailoken

Reputation: 95

Creating a Nested List with an Object Array

I have a set of SQL data which will change frequently and I need to create a nested, unordered list from that on a regular basis. I have converted the SQL data into an object array, but I'm stuck on the logic for creating the unordered list from that.

I tried making a recursive function, but I'm not experienced enough with those, so

The data contains these fields:

ID  Category Name   ParentID

So, each category has an ID and a ParentID (which matches the ID of another category). Root categories all have the same dummy ParentID.

The javascript object has the same properties. Here's an example:

var Categories = [
new Category(1, 'Root', 0),
new Category(2, 'Cat1', 1),
new Category(3, 'Cat2', 2),
new Category(4, 'Cat3', 5),
new Category(5, 'Cat4', 1),
new Category(6, 'Cat5', 5),
new Category(7, 'Cat6', 5),
new Category(8, 'Cat7', 1),
new Category(9, 'Cat8', 2),
new Category(10, 'Cat9', 1),
new Category(11, 'Cat10', 10),
new Category(12, 'Cat11', 1),
new Category(13, 'Cat12', 8)
]

I need to use that object array to make an unordered list that would look like this:

<ul>
<li>Cat1
    <ul>
        <li>Cat2</li>
        <li>Cat8</li>
    </ul>

<li>Cat4
    <ul>
        <li>Cat3</li>
        <li>Cat5</li>
        <li>Cat6</li>
    </ul>
</li>
<li>Cat7
    <ul>
        <li>Cat12</li>
    </ul>
</li>
<li>Cat8</li>
<li>Cat9
    <ul>
        <li>Cat10</li>
    </ul>
</li>
<li>Cat11</li>
</ul>

Currently, the deepest my data goes is 3 tiers, but I would like to be able to have the script do any number of tiers.

jQuery is OK for this.

Upvotes: 8

Views: 7687

Answers (4)

HenryW
HenryW

Reputation: 3761

This is what i use, with it you can have multiple root parents.

JS

    function loadCatList() {
var li_str = '';

db.transaction(function (tx) {
tx.executeSql('SELECT * FROM categories c,categories_description cd WHERE c.categories_id = cd.categories_id ORDER BY categories_id', [], function (tx, results) {

var len = results.rows.length, i;
var categories=[];
for (i = 0; i < len; i++) {

var r = results.rows.item(i);

categories[r.categories_id] = r;
var catID = r.categories_id;


}

generate_tree_list=function(array, parent, level){
var has_children = false;

  for(key in array)
  {
   var value=array[key];

    if (value['parent_id'] == parent) 
    {     


      if (has_children === false)
      {

        has_children = true;  

 if(level === 0){

 li_str += '<ul>';

 }else{

 li_str += '<ul>';

 }

 level++;

 }
 li_str += '<li id="'+ value['categories_id'] +'"><a href="productsList.html?cPath='+ value['categories_id'] +'"  data-ajax="false"><h3>' + value['categories_name'] + '</h3><p>' + value['categories_image'] + '</p></a>'

 generate_tree_list(array, key, level); 

 li_str += '</li>';

 }

 }

if (has_children === true) li_str += '</ul>';

}
generate_tree_list(categories,0,0);
$('#parents').html(li_str);





});

});  

}

HTML

<div id="catItems"><script>loadCatList();</script>

 <div id="parents"></div>

Upvotes: 0

Oriol
Oriol

Reputation: 288100

Demo: http://jsfiddle.net/4JpsW/

function Category(id, categoryName, parentId){
    this.id = id;
    this.categoryName = categoryName;
    this.parentId = parentId;
}
var categories = [
new Category(1, 'Root', 0),
new Category(2, 'Cat1', 1),
new Category(3, 'Cat2', 2),
new Category(4, 'Cat3', 5),
new Category(5, 'Cat4', 1),
new Category(6, 'Cat5', 5),
new Category(7, 'Cat6', 5),
new Category(8, 'Cat7', 1),
new Category(9, 'Cat8', 2),
new Category(10, 'Cat9', 1),
new Category(11, 'Cat10', 10),
new Category(12, 'Cat11', 1),
new Category(13, 'Cat12', 8)
];

categories.sort(function(a,b){
    return a.parentId < b.parentId ? -1 : (a.parentId > b.parentId ? 1 : 0);
});
var root = document.createElement('ul'),
    currentParentId=1,
    currentParentUl=root;
for(var i=1; i < categories.length; ++i){
    if(categories[i].parentId !== currentParentId){
        currentParentId = categories[i].parentId;
        currentParentUl = document.createElement('ul');
        root.getElementsByClassName('category_'+currentParentId)[0]
            .appendChild(currentParentUl);
    }

    currentParentUl.innerHTML += '<li class="category_'+categories[i].id+'">'+categories[i].categoryName+'</li>';
}
document.body.appendChild(root);

Note 1: Like @DanielGimenez's code, the depth is unlimited and a parent must exist before a child is added.

Note 2: My code uses the native functions sort and getElementsByClassName to do the most expensive part (sorting and finding) to have better performance.

Note 3: I have used classes instead of ids to avoid conflicts with other parts of your page. But if that's not a problem, using ids will increase the speed.

You can compare performance here: http://jsperf.com/creating-a-nested-list-with-an-object-array

Upvotes: 3

darkmirage
darkmirage

Reputation: 36

Short and dirty jQuery. This destroys the array. Not that efficient but it's short. http://jsfiddle.net/4JpsW/

function drawList(cat) {
  r = $('<div></div>').attr('data-id', '1');
  r.append($('<ul></ul>'));
  $('body').append(r)

  while (cat.length > 0) {
    if (cat[0].parent == 0) {
      cat.splice(0, 1);
      continue;
    }

    var i = 0;
    while ($('*[data-id=' + cat[i].parent + ']').length == 0) {
      i++;
    }

    elem = $('<li></li>')
      .attr('data-id', cat[i].id)
      .text(cat[i].name);

    parent_elem = $('*[data-id=' + cat[i].parent + ']')
    if (parent_elem.find('>ul').length == 0)
      parent_elem.append($('<ul></ul>'));

    parent_elem.find('>ul').append(elem);

    cat.splice(i, 1);
  }
}

Upvotes: 0

Daniel Gimenez
Daniel Gimenez

Reputation: 20494

Here is a class based approach. The depth is unlimited. The only requirement is that a parent must exist before a child is added.

// create class
// parent is optional Category
var Category = function (id, name, parent) {
    this.id = id;
    this.name = name;
    this.parent = null;
    this.children = [];

    if (parent) {
        parent.add(this);
    } 

};
Category.prototype.root = function() {
    if (this.parent) return this.parent.root();
    return this;
}
// find by id
Category.prototype.find = function (id) {
    if (this.id == id) return this;
    var found;

    for (var i = 0, il = this.children.length; i < il; i++) {
        if (found = this.children[i].find(id)) return found;
    }
    return null;
};

// create relationship
Category.prototype.add = function (cat) {
    cat.parent = this;
    this.children.push(cat);
};

// render list for item
Category.prototype.renderList = function ($parentElem) {
    var $nameElem = $('<li>' + this.name + '</li>').appendTo($parentElem);
    if (this.children.length) {
        this.renderChildren($('<ul />').appendTo($nameElem))
    }
}

// create child elements and add them to the parent
Category.prototype.renderChildren = function ($parentElem) {
    for (var i = 0, il = this.children.length; i < il; i++) {
        this.children[i].renderList($parentElem);
    }
}

function createCategory(id, name, parentId) {
    rootCat.find(parentId).add(new Category(id, name));
}
// add items
var rootCat = new Category(1, 'root');
createCategory(2, 'Cat1', 1);
createCategory(3, 'Cat2', 2);
createCategory(4, 'Cat3', 3);
createCategory(14, 'Cat3.5', 4);
createCategory(5, 'Cat4', 1);
createCategory(6, 'Cat5', 5);
createCategory(7, 'Cat6', 5);
createCategory(8, 'Cat7', 1);
createCategory(9, 'Cat8', 2);
createCategory(10, 'Cat9', 1);
createCategory(11, 'Cat10', 10);
createCategory(12, 'Cat11', 1);
createCategory(13, 'Cat12', 8);

// render
rootCat.renderChildren($('#cats'));

jsFiddle

Upvotes: 4

Related Questions