Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested Category Search using underscore js

This is how my data object would look like http://jsfiddle.net/303tpLtz/1

As you may see here there are categories inside categories which must be considered when the search is done

So the problem is I can find the top level names of the categories using _.findWhere(result.response.categories, {name: 'Arts & Entertainment'})

But the problem is when I need to find something mentioned inside the supplied request, For example if I need to find a restaurant which is inside Food >

Can anyone help me with the Deep search function please ?

My tangled solution jsFiddle:

function findDeep(cats, attrs) {
    _.each(cats, function(data, i) {
        var copy = data;
        // delete copy.categories;
        newArr.push(copy);
        _.each(data.categories, function(newObj) {
            var copy2 = newObj;
            // delete copy2.categories;
            newArr.push(copy2)

            if (newObj.categories) {
                _.each(newObj.categories, function(otherObj) {
                    var copy3 = otherObj;
                    // delete copy3.categories;
                    newArr.push(copy3)
                })
            }
        })
    });

    return _.findWhere(newArr, attrs) || null;
}
like image 616
Sahan Avatar asked Feb 18 '26 06:02

Sahan


1 Answers

The problem with this data is each node potentially should be inspected further, which means you can't neatly apply a filter to every item, because the skipped items themselves might have nested categories you need to inspect.

However, using plain javascript or alternatively with the help of _.reduce and a bit of recursive magic, we can get the job done with only a little bit of code.

jsFiddle

function findMatchingCategories(cats, attrs) {
    return _.filter(extractCategories(cats), attrs);

    function extractCategories(cats) {
        return _.reduce(cats, function (result, value) {
            return result.concat([value]).concat(value.categories ? extractCategories(value.categories) : []);
        }, []);
    }
}

console.log(findMatchingCategories(data, { name: 'Tunnel' }));

To explain:

_.reduce let's you go through a set of data and keep track of a progressively 'reduced' data variable. In our case, we reduce a set of categories to a new array called result which only contains all nested categories. To make sure we inspect all nested categories as well, we recursively call extractCategories and use it's result to add it to the reduced result array.

Finally what we are left with are all categories, nested or not, which we then filter based on attr matches.


A more performant version with less concatenation:

jsFiddle

function findMatchingCategories(cats, attrs) {
    return _.filter(extractCategories(cats, []), attrs);

    function extractCategories(currentCats, allCats) {
        _.each(currentCats, function (value) {
            allCats.push(value);
            if (value.categories) {
                extractCategories(value.categories, allCats);
            }
        }, []);
        return allCats;
    }
}

console.log(findMatchingCategories(data, { name: 'Tunnel' }));

The more we go for performance, the less concise the code becomes.

Performance comparison of all three approaches

like image 124
Benny Bottema Avatar answered Feb 20 '26 21:02

Benny Bottema



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!