Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom filter vs filter function in controller performance comparison

Suppose I have an array of 5000 objects (with boolean values) which I have to ng-repeat in the template:

$scope.arr = [
    {
        "value": true
    },
    {
        "value": false
    },
    {
        "value": false
    }
    //and so on
]

Now, I want to filter this ng-repeated array on the basis of a dynamic variable, say 'show_filter', which I am setting elsewhere.

If 'show_filter' is set to 'all' I want to show all the objects. If it is set to false (the boolean value) then I want to show objects with 'value' key set to false. Same goes for when 'show_filter' is set to true.

So, there are two approaches:

1. Build a custom filter:

I would write a custom filter for the filtering task like this:

filter:

app.filter('filterArr', function() {
    return function(arr, show_filter) {
        var filtered_arr = [];
        if(show_filter != 'All') { //if show_filter is a boolean value
            for(var i = 0; i < arr.length; i++) {
                if(arr[i].value == show_filter) { 
                    filtered_arr.push(arr[i]);
                }
            }
            return filtered_arr;
        }
        else {
            return arr; //return the entire array if show_filter is set to 'All'
        }
    }
})

template:

obj in arr | filterArr : show_filter

2. Write a filter function in the controller:

filter:

$scope.filterObjects = function(arr) {
    var filtered_arr = [];
    if($scope.show_filter != 'All') { //if $scope.show_filter is a boolean value
        for(var i = 0; i < arr.length; i++) {
            if(arr[i].value == $scope.show_filter) { 
                filtered_arr.push(arr[i]);
            }
        }
        return filtered_arr;
    }
    else {
        return arr; //return the entire array if show_filter is set to 'All'
    }
}

template:

obj in filterObjects(arr)

Which of the above two methods will be faster? I have seen the custom filter code execute everytime for each digest loop and not only for changes made to $scope.show_filter, which leds me to believe its quite inefficient. Although I am not sure which is faster between the two ways.

like image 960
Tarun Dugar Avatar asked Nov 21 '15 08:11

Tarun Dugar


1 Answers

Both functions will be called in every digest cycle. That's somewhat obvious for the second function. The return value of filterObjects(arr) could be different on every call.

It's not so obvious why a filter would be called in every digest cycle. The documentation states the following:

The filter function should be a pure function, which means that it should be stateless and idempotent. Angular relies on these properties and executes the filter only when the inputs to the function change.

So if neither arrnor show_filter change then the filter shouldn't be called, right? But here's the catch: Detecting a change in arr is costly.

Angular has to make a copy of the array to compare it with the current content. Even if nothing has changed, every single item has to be compared. And if the items are objects every single property of them has to be compared. Directly calling a filter instead is much cheaper. And that's what Angular does when a filter is applied to an array (or object).

To speed up the application you have two choices. The first one is to filter the array only when it's necessary and to expose the filtered array to ng-repeat. E.g. if you can enter a value by which the array will be filtered, then filter the array whenever that value changes.

The second alternative can be used if both the array and the filter don't change (so not in your case). Then you can use one-time binding:

<li ng-repeat="item in ::array | filter">

That's useful when you have a fixed set of items and want to sort them by name e.g. The filter will be called only once in that case.

like image 102
a better oliver Avatar answered Sep 25 '22 21:09

a better oliver