Creating a tree from a flat list using lodash

Your code is very imperative. Try focusing on the "big picture" of data flow instead of writing code by trial-and-error. It's harder, but you get better results (and, in fact, usually it's faster) :)

My idea is to first group the categories by their parents. This is the first line of my solution and it actually becomes much easier after that.

_.groupBy and _.keyBy help a lot here:

function makeCatTree(data) {
    var groupedByParents = _.groupBy(data, 'parent');
    var catsById = _.keyBy(data, 'id');
    _.each(_.omit(groupedByParents, ''), function(children, parentId) {
        catsById['c' + parentId].children = children; 
    });
    _.each(catsById, function(cat) {
        // isParent will be true when there are subcategories (this is not really a good name, btw.)
        cat.isParent = !_.isEmpty(cat.children); 
        // _.compact below is just for removing null posts
        cat.children = _.compact(_.union(cat.children, cat.posts));
        // optionally, you can also delete cat.posts here.
    });
    return groupedByParents[''];
}

I recommend trying each part in the developer console, then it becomes easy to understand.


I have made a small fidde that I think that is what you want.

http://jsfiddle.net/tx3uwhke/

var tree = buildTree(getData());
var pre = document.getElementById('a');
var jsonString = JSON.stringify(tree, null, 4);
console.log(jsonString);
pre.innerHTML = jsonString;

document.body.appendChild(pre);

function buildTree(data, parent){
    var result = [];
    parent = typeof parent !== 'undefined' ? parent : {id:""};
    children = _.filter(data, function(value){
        return value.parent === parent.id;
    });
    if(!_.isEmpty(children)){
        _.each(children, function(child){
            if (child != null){
                result.push(child);
                if(!_.isEmpty(child.posts)){
                    var posts = _.filter(child.posts, function(post){
                        return post !== null && typeof post !== 'undefined';
                    });
                    if(!_.isEmpty(posts)){
                        _.forEach(posts, function(post){
                            post.isParent = false;
                        });
                    }
                    result = _.union(result, posts);
                    delete child.posts;
                }
                ownChildren = buildTree(data, child);
                if(!_.isEmpty(ownChildren)){
                    child.isParent = true;
                    child.children = ownChildren;
                }else{
                    child.isParent = false;
                }
            }
        });
    }
    return result;
}

EDIT: made a new fiddle to contain the isParent part you can find it here