2
\$\begingroup\$

I have a $scope.users that I use to show a table and I need to apply a filter with multiple options,

Here is the $scope.users object:

[
  {
    "login": "[email protected]",
    "merchant": {
      "merchantId": 500095,
      "status": "ENABLED",
      "defaultAccountId": null,
      "accounts": {
        "500092": "ADMIN",
        "500178": "CONSULT",
        "500180": "ADMIN",
        "500182": "ADMIN",
        "500201": "ADMIN"
      }
    },    
    "name": "Carlos"    
  },
  {
    "login": "[email protected]",
    "merchant": {
      "merchantId": 500095,
      "status": "DISABLED",
      "defaultAccountId": null,
      "accounts": {
        "500092": "CONSULT",
        "500178": "MANAGER",
        "500180": "ADMIN",
        "500182": "ADMIN",
        "500201": "ADMIN"
      }
    },
    "name": "Carlos Andres"
  },
  {
    "login": "[email protected]",
    "merchant": {
      "merchantId": 500095,
      "status": "DISABLED",
      "defaultAccountId": null,
      "accounts": {
        "500092": "CONSULT",
        "500178": "MANAGER",
        "500180": "ADMIN",
        "500182": "ADMIN",
        "500201": "ADMIN"
      }
    },
    "name": "Cosme"    
  }
]

In the filter I pass other object from the $scope to filter the data, in this case is $scope.searchParams that I previously calculate in a directive.

$scope.searchParams:

{
  "name": [
    "arl"
  ],
  "login": [
    "@"
  ],
  "merchant.status": [
    "ENABLED"
  ],
  "merchant.accounts": [
    "501678",
    "501675",
    "MANAGER",
    "ADMIN"
  ]
}

As you can see, name and login are unique in the filter, but status and account can have multiple values to apply in the filter. Here is my approach to do that but I think is very ineffective, it is important that each filter is applied on the data previously filtered by another filter:

var usrUser = function () {

  var SEPARATOR = '.';

  return function (users, filters) {

    // when the filter is not defined shows all results
    if(filters === undefined || angular.equals({}, filters)){
      return users;
    }

    var matchName = function (element){
      if(element['name'].indexOf(filters['name']) >= 0){
        return element;
      }
    };

    var matchLogin = function (element){
      if(element['login'].indexOf(filters['login']) >= 0){
        return element;
      }
    };

    var matchState = function (element){
      if(filters['merchant.status'].indexOf(element['merchant']['status']) >= 0){
        return element;
      }
    };

    var matchAccount = function (element){
      var match = false;
      angular.forEach(element['merchant']['accounts'], function(profile, accountId){
        if(filters['merchant.accounts'].indexOf(accountId) >= 0 ||
          filters['merchant.accounts'].indexOf(profile) >= 0){
          match = true;
          return;
        }
      });
      if(match){
        return element;
      }
    };

    if(filters['name'] !== undefined){
      users = users.filter(matchName);
    }

    if(filters['login'] !== undefined){
      users = users.filter(matchLogin);
    }

    if(filters['merchant.status'] !== undefined){
      users = users.filter(matchState);
    }

    if(filters['merchant.accounts'] !== undefined){
      users = users.filter(matchAccount);
    }

    return users;
  };
};
//-- register filter
angular.module('users').filter('usrUser', usrUser);

Finally here is how I am using the fiter: <tbody ng-repeat="user in users | secUser:searchParams " class="table-striped">

Is there a better way to implement this filter considering that in the future there can be more filter fields? Also I think is very coupled with the view.

\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

In general, your data is structured very poorly for your filtering use cases. Having to iterate an entire array of users every time you want to apply a filter (and then having some of those filter methods further iterate nested data structures) will become a very slow process once you have a large number of data objects that you want to filter (not to mention also apply multiple filters towards).

It is also unclear how your filtering logic support AND vs. OR across the filtering criteria, so not sure how flexible/extensible what you have really is.

Finally, the approach you take on chaining the filters might need to be something you think about optimizing at some point. If you have fields that are unique, like login presumably is, these would almost seem to need different filter treatment than trying to iterate through all users making this comparison. These sorts of filters should be binary in the sense that the match will either return exactly one item or no items if the match isn't fulfilled. So, this is not so much a filter as it is an indexed lookup. This ties back to my concern about your data structure. If you have unique fields, you should be able to perform an O(1) lookup to determine if that object with that unique value exists, not have to perform an O(n) iteration across all objects to look for this unique value.

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.