Is it possible to filter angular.js by containment in another array?
Asked Answered
A

3

21

So if I have an array:

$scope.letters = 
[{"id":"a"},
{"id":"b"},
{"id":"c"}];

And another array

$scope.filterBy = ["b","c","d"];

And I want to have some ng-repeat to filter $scope.letters by only items that appear in $filterBy.

I want to be able to do something to the effect of:

<span ng-repeat="{{letter in letters|filter: letter.id in filterBy }} > {{letter.id}} </span>

And have it print b,c

I know this is a really stupid example, but is there a way to filter an angular.js expression based on the contents of another array object?

Adjudge answered 16/3, 2013 at 21:26 Comment(0)
S
29

You should try something like that:

JS:

angular.module('Test', []);

function Ctrl($scope) {
  $scope.letters = [
    {id: 'a'},
    {id: 'b'},
    {id: 'c'}
  ];

  $scope.filterBy = ['b', 'c', 'd'];

  $scope.filteredLetters = function () {
    return $scope.letters.filter(function (letter) {
      return $scope.filterBy.indexOf(letter.id) !== -1;
    });
  };
}

Ctrl.$inject = ['$scope'];

HTML:

<div ng-repeat='letter in filteredLetters(letters)'>{{letter.id}}</div>

You can try live example.

Sackett answered 16/3, 2013 at 21:45 Comment(3)
filteredLetters() will get called every digest cycle. So if you have in input field using ng-model in your view, that means every keystroke. It would be better to store the results of the filter on a new $scope property. Use $watch()es to update the filter results if letters or filterBy change.Fenestella
While it will work (provided your app is very simple) I wouldn't recommend using this approach - as @MarkRajcok mentions, it's called each digest cycle meaning performance is horrible and I was getting errors from angular similar to "Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!"Archetype
Really helped me.Thanks!Ashtonashtonunderlyne
B
36

Update

Here's an angular module (based on @InviS answer) to easily implement this filter inside your angular application: filters-inArrayFilter


Here's the angular filters approach based on @InviS answer:

The filter should be like this:

.filter('inArray', function($filter){
    return function(list, arrayFilter, element){
        if(arrayFilter){
            return $filter("filter")(list, function(listItem){
                return arrayFilter.indexOf(listItem[element]) != -1;
            });
        }
    };
});

where list is the list you're filtering (this param is set by default by angular), arrayFilter is the array you're using as filter, and element is the name of the property to filter in your list.

To use this filter you use your ng-repeat as:

<div ng-repeat='letter in letters | inArray:filterBy:"id"'>{{letter.id}}</div>

where inArray is the filter, filterBy (the first argument of this filter) is your array to match against, and "id" (second argument) is the element of the list you want to match against the array.

You can try this live example using the angular filters approach.

Betterment answered 16/1, 2014 at 20:12 Comment(9)
What if i have to filter multiple columns ? Would chaining do ?Ferren
@Ferren What do you mean by multiple columns?Betterment
@Betterment how would you filterby if object key was a number? use $index?Photocurrent
@Photocurrent Don't get your question, you mean filter an array of arrays?Betterment
@Betterment presumably this could also be done with 2 objects, filter one with by the other? Looking for the modification.Photocurrent
@Cyberdelphos, the link to angular-inarray-filter is 404-ing.Archetype
@A.Murray Solved it! Thanks!Betterment
This is working perfect for me. How could i return everything when the array is empty?Cobham
@Cobham If you mean if "filterBy" is empty, you could add an else for the if statement and put a return true so all of the elements pass the filter, can't test it right now but it would be something like it.Betterment
S
29

You should try something like that:

JS:

angular.module('Test', []);

function Ctrl($scope) {
  $scope.letters = [
    {id: 'a'},
    {id: 'b'},
    {id: 'c'}
  ];

  $scope.filterBy = ['b', 'c', 'd'];

  $scope.filteredLetters = function () {
    return $scope.letters.filter(function (letter) {
      return $scope.filterBy.indexOf(letter.id) !== -1;
    });
  };
}

Ctrl.$inject = ['$scope'];

HTML:

<div ng-repeat='letter in filteredLetters(letters)'>{{letter.id}}</div>

You can try live example.

Sackett answered 16/3, 2013 at 21:45 Comment(3)
filteredLetters() will get called every digest cycle. So if you have in input field using ng-model in your view, that means every keystroke. It would be better to store the results of the filter on a new $scope property. Use $watch()es to update the filter results if letters or filterBy change.Fenestella
While it will work (provided your app is very simple) I wouldn't recommend using this approach - as @MarkRajcok mentions, it's called each digest cycle meaning performance is horrible and I was getting errors from angular similar to "Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!"Archetype
Really helped me.Thanks!Ashtonashtonunderlyne
S
6

Quite old but I needed it and I had to change it a bit. Here's my filter "notInArray"

app.filter('notInArray', function($filter){
return function(list, arrayFilter, element){
    if(arrayFilter){
        return $filter("filter")(list, function(listItem){
          for (var i = 0; i < arrayFilter.length; i++) {
              if (arrayFilter[i][element] == listItem[element])
                  return false;
          }
          return true;
        });
    }
};

});

<md-chips ng-model="filter.SelectedValues" md-autocomplete-snap
          md-require-match="true">
      <md-autocomplete
          md-search-text="searchFilterChip"
          md-items="val in filter.Values | notInArray:filter.SelectedValues:'Id'"
          md-item-text="val.Name"
          md-no-cache="true"
          md-min-length="0">
        <span md-highlight-text="searchFilterChip">{{val.Name}}</span>
      </md-autocomplete>
      <md-chip-template>
        {{$chip.Name}}
      </md-chip-template>
  </md-chips>

I supposed this can be improved but not needed in my case.

Hope that helps someone !

Snuggle answered 12/2, 2016 at 13:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.