afreeland
afreeland

Reputation: 3979

Recursion maintaining ancestors/parents nested object in JavaScript

I have a very deep nested category structure and I am given a category object that can exist at any depth. I need to be able to iterate through all category nodes until I find the requested category, plus be able to capture its parent categories all the way through.

Data Structure

[
{
    CategoryName: 'Antiques'
},
{
    CategoryName: 'Art',
    children: [
        {
            CategoryName: 'Digital',
            children: [
                {
                    CategoryName: 'Nesting..'
                }
            ]
        },
        {
            CategoryName: 'Print'
        }
    ]
},
{
    CategoryName: 'Baby',
    children: [
        {
            CategoryName: 'Toys'
        },
        {
            CategoryName: 'Safety',
            children: [
                {
                    CategoryName: 'Gates'
                }
            ]
        }
    ]
},
{
    CategoryName: 'Books'
}

]

Code currently in place

function findCategoryParent (categories, category, result) {
    // Iterate through our categories...initially passes in the root categories
    for (var i = 0; i < categories.length; i++) {

        // Check if our current category is the one we are looking for
        if(categories[i] != category){
            if(!categories[i].children)
                continue;

            // We want to store each ancestor in this result array
            var result = result || [];
            result.push(categories[i]);
            // Since we want to return data, we need to return our recursion
            return findCategoryParent(categories[i].children, category, result);
        }else{
            // In case user clicks a parent category and it doesnt hit above logic
            if(categories[i].CategoryLevel == 1)
                result = [];

            // Woohoo...we found it
            result.push(categories[i]);
            return result;
        }
    }
}

Problem

  1. If I return my recursive function it will work fine for 'Art' and all of its children..but since it returns, the category Baby never gets hit and therefor would never find 'Gates' which lives Baby/Safety/Gates

  2. If I do not return my recursive function it can only return root level nodes

Would appreciate any recommendations or suggestions.

Upvotes: 0

Views: 1733

Answers (1)

afreeland
afreeland

Reputation: 3979

Alright, I believe I found a solution that appears to work for my and not sure why my brain took so long to figure it out...but the solution was of course closure.

Essentially I use closure to keep a scoped recursion and maintain my each iteration that it has traveled through

var someobj = {
    find: function (category, tree, path, callback) {
        var self = this;
        for (var i = tree.length - 1; i >= 0; i--) {

            // Closure will allow us to scope our path variable and only what we have traversed
            // in our initial and subsequent closure functions
            (function(){
                // copy but not reference
                var currentPath = path.slice();

                if(tree[i] == category){
                    currentPath.push({name: tree[i].name, id: tree[i].id});
                    var obj = {
                        index: i,
                        category: category,
                        parent: tree,
                        path: currentPath
                    };
                    callback(obj);
                }else{
                    if(tree[i].children){
                        currentPath.push({name: tree[i].name, id: tree[i].id});
                        self.find(category, tree[i].children, currentPath, callback);
                    }
                }

            })(tree[i]);
        }
    },

    /**
     * gets called when user clicks a category to remove
     * @param  {[type]} category [description]
     * @return {[type]}          [description]
     */
    removeCategory: function (category) {
        // starts the quest for our category and its ancestors
        // category is one we want to look for
        // this.list is our root list of categoires,
        // pass in an intial empty array, each closure will add to its own instance
        // callback to finish things off
        this.find(category, this.list, [], function(data){
            console.log(data);
        });
    }
}

Hope this helps others that need a way to traverse javascript objects and maintain parent ancestors.

Upvotes: 1

Related Questions