9

I'm working through the AngularJS tutorial, and understand the basics of

However, the out of the box implementation seems limited to just filter the list of items to the exact word or phrase entered in .

Example: if the query is "table cloth", the result list can include a result with this phrase, "Decorative table cloth", but won't include "Decorative cloth for table" because the filter is just a continuous search string.

I know there's the ability to add custom filters, but at first glance it seems like those are mainly transforms.

Is there any way to add a custom filter so that both "Decorative cloth for table" and "Decorative table cloth" show up in the filtered result set?

3
  • would have to create custom filter that looks for individual words, not full string. Might also consider using a fuzzy search library also. Commented Dec 8, 2013 at 22:12
  • Related: stackoverflow.com/questions/17994699/… Commented Dec 8, 2013 at 22:13
  • with custom filter you can echieve everything you want, any regex and statements Commented Dec 8, 2013 at 22:14

6 Answers 6

19

Some improvements to the above custom filter:

Instead of using a loop within a loop, counts, and indexOf, this one uses regular expressions to achieve a logical AND and also a logical OR depending on the third argument to the filter (input array of strings, search terms, AND or OR).

Have a look at the forked Fiddle with the two types of filter and results:

http://jsfiddle.net/jonjon/Cx3Pk/23/

angular.module('app', [])
    .filter("myFilter", function () {
    return function (input, searchText, AND_OR) {
        var returnArray = [],
            // Split on single or multi space
            splitext = searchText.toLowerCase().split(/\s+/),
            // Build Regexp with Logical AND using "look ahead assertions"
            regexp_and = "(?=.*" + splitext.join(")(?=.*") + ")",
            // Build Regexp with logicial OR
            regexp_or = searchText.toLowerCase().replace(/\s+/g, "|"),
            // Compile the regular expression
            re = new RegExp((AND_OR == "AND") ? regexp_and : regexp_or, "i");

        for (var x = 0; x < input.length; x++) {
            if (re.test(input[x])) returnArray.push(input[x]);
        }
        return returnArray;
    }
});
Sign up to request clarification or add additional context in comments.

6 Comments

This is the superior answer, it is faster.
@MathewBerg Thanks for noticing. Regular expressions are often overlooked by JS programmers but they definitely are as useful today as they were when Perl was the main scripting language of the web.
I'm trying to implement this, but running into problems (no results). Could it be that my input is a large array of objects?
@bluemihai The "input" argument is an array of strings in this example, so you have to provide the expected argument type for this function to work. This could be done by filtering your input and extracting the strings or group of strings on which you want to perform a boolean search.
The only one that actually worked for me. Thank You.
|
7

Please see surfbuds answer below as it is superior

Just roll with your own filter:

.filter("myFilter", function(){
    return function(input, searchText){
        var returnArray = [];
        var searchTextSplit = searchText.toLowerCase().split(' ');
        for(var x = 0; x < input.length; x++){
            var count = 0;
            for(var y = 0; y < searchTextSplit.length; y++){
                if(input[x].toLowerCase().indexOf(searchTextSplit[y]) !== -1){
                    count++;
                }
            }
            if(count == searchTextSplit.length){
                 returnArray.push(input[x]);   
            }
        }
        return returnArray;
    }
});

jsfiddle: http://jsfiddle.net/Cq3PF/

This filter makes sure that all search words are found.

1 Comment

Thanks very much to the commenters and the answerer...awesome stuff, guys!
3

Alternatively you could use the default Angular filter within your custom filter like so:

angular.module('app').filter("multiWordFilter", function($filter){
    return function(inputArray, searchText){
        var wordArray = searchText ? searchText.toLowerCase().split(/\s+/) : [];
        var wordCount = wordArray.length;
        for(var i=0;i<wordCount;i++){
            inputArray = $filter('filter')(inputArray, wordArray[i]);
        }
        return inputArray;
    }
});

This could be embellished further with user2005009's AND_OR comparator.

Comments

0

Here's my version. It uses JonoWilko's method of using the built in filterFilter combined with surfbud's AND/OR flag (defaulted to "AND").

JavaScript

angular.module('filters').filter('searchFilter', function($filter) {

    return function(inputArray, searchText, booleanOp) {
        booleanOp = booleanOp || 'AND';

        var searchTerms = (searchText || '').toLowerCase().split(/\s+/);

        if (booleanOp === 'AND') {
            var result = inputArray;
            searchTerms.forEach(function(searchTerm) {
                result = $filter('filter')(result, searchTerm);
            });

        } else {
            var result = [];
            searchTerms.forEach(function(searchTerm) {
                result = result.concat($filter('filter')(inputArray, searchTerm));
            });
        }

        return result;
    };
});

CoffeeScript

angular.module('filters').filter 'searchFilter', ($filter)->

    (inputArray, searchText='', booleanOp = 'AND')->
        searchTerms = searchText.toLowerCase().split(/\s+/)

        if booleanOp is 'AND'
            result = inputArray
            searchTerms.forEach (word)->
                result = $filter('filter')(result, word)

        else
            result = []
            searchTerms.forEach (word)->
                result = result.concat $filter('filter')(inputArray, word)

        result

'AND' Usage (default)

<div ng-repeat="product in products | searchFilter: searchInputText"></div>

'OR' Usage

<div ng-repeat="product in products | searchFilter: searchInputText : 'OR'"></div>

Comments

0

You can do a multiple word search on a object as follows:

.filter("myFilter", function(){
    return function(input, searchText){
         var returnArray = [];
         var searchTextSplit = searchText.toLowerCase().split(' ');
        for(var x = 0; x < input.length; x++){
             var count = 0;
            for(var y = 0; y < searchTextSplit.length; y++){
                angular.forEach(input[x], function(item){
                    if(item.toLowerCase().indexOf(searchTextSplit[y]) !== -1){
                        count++;
                    }
                });

            }
            if(count == searchTextSplit.length){
                 returnArray.push(input[x]);   
            }
        }
        return returnArray;
    }
});    

Working demo in js fiddle

2 Comments

In the future, please edit in the relevant code as text in your post. External links are no substitute for code in the post itself.
How would you implement this type of search? In this example in their table you can type part of a word, hit the space key, and type part of another word in a column, and it still filters accordingly. datatables.net
0

Not a one liner but still quite short and fun

app.filter("filterall",function($filter) {
    return function(arr,t){
        (t?t.split(/\s+/):[]).forEach(function(v){ arr = $filter('filter')(arr,v); });
        return arr;
    };
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.