Richard Richard - 7 months ago 13
Javascript Question

Angular Implementing multi search filter

I am using the angular advanced search box and I want to implement a custom filter in my angular page but I am having trouble figuring out how to handle a string of requirements in a query. Let's say I have several objects that follow the following format:

{
"displayName":"John",
"gender":"male",
"type":"customer",
"items": 3,
}


I would want to be able to search in plain english `Anyone who's name is John and is of type Customer". Here is my angular search code so far:

app.filter('infoFilter', function() {
return function(data, query) {
var output = [];
var index;

//loop over the original array
angular.forEach(data, function(row, index) {
angular.forEach(query, function(input, value) {
if(input) {
if(angular.isNumber(row[value]) && row[value] == input) {
output.push(row);
} else if(!angular.isNumber(row[value]) && row[value].toLowerCase().indexOf(input.toLowerCase()) > -1) {
output.push(row);
}
}
});
});
if(query) {
return data;
} else {
return output;
}
}
});


The query comes in as an object that looks like this:

{
"displayName":"John"
}


This works perfectly fine for 1 search parameter. So if I searched for
John
my table would update to show all entries with the name of john. However, this wouldn't really work for multi search parameters. So if the query looked like this:

{
"displayName":"John",
"gender":"Female"
}


I need to apply all the parameters at once before i do
output.push(row)
. How exactly would I go about doing this?

Answer

If I understand you correctly you want to filter the rows where all query parameters apply (AND). I modified your code slightly to achieve this behavior.

app.filter('infoFilter', function() {
    return function(data, query) {
        var output = [];
        var index;

        //loop over the original array
        angular.forEach(data, function(row, index) {

            var pushRow = true;

            angular.forEach(query, function(input, value) {
                if(input) {
                    if(angular.isNumber(row[value]) && row[value] == input) {
                        return;
                    } else if(!angular.isNumber(row[value]) && row[value].toLowerCase().indexOf(input.toLowerCase()) > -1) {
                        return;
                    }
                }
                pushRow = false;
            });

            if (pushRow) {
                output.push(row);
            }
        });

        // This bit also seems to be the wrong way around in your code.
        if(!query) {
            return data;
        } else {
            return output;
        }
    }
});

Edit:

Here is also an optimized version of the same filter using javascripts built in array functions.

app.filter('infoFilter', function() {
    return function(data, query) {
        if(!query || !data) {
            return data;
        }

        return data.filter(function(row) {
            return Object.keys(query).every(function(key) {
                var rowValue = row[key];
                var queryValue = query[key];

                return (angular.isNumber(rowValue) && rowValue == input) ||
                        (angular.isString(rowValue) && rowValue.toLowerCase().indexOf(queryValue.toLowerCase()) > -1);
            });
        });
    };
});