Martin
Martin

Reputation: 57

Filter filtered results function AngularJS ngrepeat

I want to filter filtered results.

I have the following JSON

$scope.JSON = [
  {
    "Animal": "Horse",
    "Status": "awake",
    "LastUpdate": {
      "$date": 1473248184519
    }
  },
  {
    "Animal": "Rabbit",
    "Status": "awake",
    "LastUpdate": {
      "$date": 1473248194240
    }
  },
  {
    "Animal": "Rabbit",
    "Status": "eating",
    "LastUpdate": {
      "$date": 1473249639255
    }
  },
  {
    "Animal": "Horse",
    "Status": "eating",
    "LastUpdate": {
      "$date": 1473249652549
    }
  },
  {
    "Animal": "Horse",
    "Status": "sleeping",
    "LastUpdate": {
      "$date": 1473249656338
    }
  }
]

and the following filter function

$scope.filtering = filtering;

function filtering(animals){
    var temp = [].concat(animals)
    var animalsExisting = []; // To keep track of animals already added
    return temp.reverse().filter(function(animal){
       var notAdded = animalsExisting.indexOf(animal.Animal) === -1;
       if(notAdded) {
           animalsExisting.push(animal.Animal);
       }
       return notAdded;
     })
}

See also this plunkr: https://plnkr.co/edit/OQVjB47bpS9fKubO2lum?p=preview

How do I filter the returned Array notAdded with the Status, e.g. I want to show only the last & "eating" animals => result: Rabbit?

Upvotes: 0

Views: 45

Answers (1)

Dennis Baizulin
Dennis Baizulin

Reputation: 1420

First option - Slow, but easy

Easiest way - using native Angular filter

<tr ng-repeat="roll in filtering(JSON) | filter : { Status: 'eating' }">


Second option - quick

Because either Angular will invoke filtering(JSON) on each $digest (which occures pretty often) it would be much better if you render a static list, not the return result of the function.

What I suggest is you rewrite your code to something along the lines

$scope.filters = {
    status: null
};

$scope.filteredAnimals = [];

$scope.getLastStatusesForEachAnimal = function() {
    var map = {};

    return [].concat($scope.JSON)
        .reverse()
        .filter(function(animal) {
            return !map[animal.Animal] ? (map[animal.Animal] = true) : false;
        });
};

$scope.filterAnimals = function() {
    $scope.filteredAnimals = $scope.getLastStatusesForEachAnimal()
        .filter(function(animal) {
            return $scope.filters.status ? animal.Status === $scope.filters.status : true;
        });
};

$scope.filterAnimals();


Third option - a tad slower, but more elegant

Write your own filter with caching, example

.filter('animalStatusFilter', function() {
    var cache = {
        params: {},
        result: null
    };

    return function(animals, status, onlyLast) {
        var params = {
            animals: animals,
            status: status,
            onlyLast: onlyLast
        };

        if (validateCache(params)) {
            return cache.result;
        }

        animals = onlyLast ? getLastStatusesForEachAnimal(animals) : animals;

        if (status) {
            animals = animals.filter(function(animal) {
                return status ? animal.Status === status : true;
            });
        }

        cache.params = params;
        cache.result = animals;

        return animals;
    };

    function validateCache(params) {
        return params.animals === cache.params.animals &&
            params.status === cache.params.status &&
            params.onlyLast === cache.params.onlyLast
    }

    function getLastStatusesForEachAnimal(animals) {
        var map = {};

        return [].concat(animals)
            .reverse()
            .filter(function(animal) {
                return !map[animal.Animal] ? (map[animal.Animal] = true) : false;
            });
    }
})

Upvotes: 1

Related Questions