Arulkumar
Arulkumar

Reputation: 13247

How to filter or custom filter array of objects based on matching values from another object

I implemented an advance search with 15 input fields in AngularJS.

In the page load itself the result set is return from database in JSON format and i need to do the filter in client side only.

The input criteria's equivalent column is available in the result set and i need to check in its respective column only.

I am converting each column by JSON.stringify() and check with the search params like the below :

$scope.filteredData = $scope.actualData.filter(function(item) {
    return JSON.stringify(item.FirstName).toLowerCase().indexOf(lowerFirstName) != -1 &&
    JSON.stringify(item.LastName).toLowerCase().indexOf(lowerLastName) != -1 &&
    JSON.stringify(item.EmailAddress).toLowerCase().indexOf(lowerEmailAddress) != -1 &&
    JSON.stringify(item.Address1).toLowerCase().indexOf(lowerAddress1) != -1 &&
    JSON.stringify(item.Address2).toLowerCase().indexOf(lowerAddress2) != -1;
    ...... etc // upto 15 fields
});

Since i have the 15 input fields and the actual result set contains a minimum of 50,000 records.

So converting each record's each column by JSON.stringify() and check with search params will surely cause the performance issue.

Is there any other way to achieve the filtering in client side with other approach.

I posted a sample code in Plunker with 5 input fields only : http://plnkr.co/edit/nUWZEbGvz7HG6gb91YZP

Upvotes: 2

Views: 1576

Answers (2)

m59
m59

Reputation: 43795

sylwester's answer is the normal way you'd filter things. Your code looks like you want to filter down to only the object that matches every input field. You code attempts to find an object where every property matches the searchParams object. At that point, I don't see what benefit there is to finding that object, because the user already created the object again! Nonetheless, here's a proper version of your code:

Live demo here.

<div ng-repeat="data in actualData | filter:searchData()">
 $scope.searchData = function() {
    return function(item) {
      return Object.keys(item).every(function(key) {
        // skip the $$hashKey property Angular adds to objects
        if (key === '$$hashKey') { return true; }
        var searchKey = key.charAt(0).toLowerCase()+key.slice(1);
        return item[key].toLowerCase() === $scope.searchParams[searchKey].toLowerCase();
      });
    };
  };

You really need to limit the data coming from the server for the browser's sake and for the server's sake. It's easy to implement a LIMIT, OFFSET system. It sounds like, overall, you just need to be able to query the server for a certain record.

From your comments, it seems you definitely want Angular's built in filter filter:searchParams, and just capitalize your searchParams models to match your data. For fun, I'll include more options for finer tuning.

This one almost mimics filter:searchParams. You can change > 1 to adjust when the partial matching kicks in, or have it return true only when both items are strictly equal === to disable partial matching. The difference here is that all items are hidden until matched, whereas filter:searchParams will show all items and then remove what doesn't match.

Live demo here.

  $scope.searchData = function() {
    return function(item) {
      return Object.keys(item).some(function(key) {
        if (key === '$$hashKey') { return false; }
        var searchKey = key.charAt(0).toLowerCase()+key.slice(1);
        var currentVal = $scope.searchParams[searchKey].toLowerCase();
        var match = item[key].toLowerCase().match(currentVal);
        return currentVal.length > 1 && match;
      });
    };
  };

Lastly, to perfectly mimic filter:searchParams, you'd just put in a check to NOT filter the items until there is user input and the input is long enough to start the partial match.

Live demo here.

  $scope.searchData = function() {
    var partialMatchLength = 2;
    return function(item) {
      var shouldFilter = Object.keys($scope.searchParams).some(function(key) {
        return $scope.searchParams[key] && $scope.searchParams[key].length >= partialMatchLength;
      });
      if (!shouldFilter) { return true; }
      return Object.keys(item).some(function(key) {
        if (key === '$$hashKey') { return false; }
        var searchKey = key.charAt(0).toLowerCase()+key.slice(1);
        var currentVal = $scope.searchParams[searchKey].toLowerCase();
        var match = item[key].toLowerCase().match(currentVal);
        return currentVal.length >= partialMatchLength && match;
      });
    };
  };

Upvotes: 2

sylwester
sylwester

Reputation: 16498

First of all you ng-repeter with 50.000 records more likely is going to kill your browser, so you should thing about pagination. Secondly you can easy filter your data using angular filter please see that demo

http://plnkr.co/edit/R8b8G4xCMSQmX1144UJG?p=preview

<div ng-controller="ListCtrl">
    <br />
    First Name:
    <input type="text" id="txtFirstname" ng-model="searchParams.FirstName">
    <br/>Last Name:
    <input type="text" id="txtLastname" ng-model="searchParams.LastName">
    <br/>Email Address:
    <input type="text" id="txtEmailAddress" ng-model="searchParams.EmailAddress">
    <br/>Address 1:
    <input type="text" id="txtAddress1" ng-model="searchParams.Address1">
    <br/>Address 2:
    <input type="text" id="txtAddress2" ng-model="searchParams.Address2">
    <br/>
    <button class="btn btn-primary" ng-click="searchData()">Search</button>
    <br />
    <hr />
    <b>Filtered Data(s):</b>
    <div ng-repeat="data in actualData | filter:searchParams ">
      <span ng-bind="data.FirstName"></span>
      <span ng-bind="data.LastName"></span> |
      Address : {{data.Address1}}
    </div>
    <hr />

  </div>

Upvotes: 1

Related Questions