0

I need to filter an array by multiple filters. All works so far. But is there a more elegant/effective way of doing this? If a filter is empty it should not be returned in the filter result:

this.filteredList = this.constList.filter(function(r) {
            if(this.regionFilterValue && this.filterVBundle && this.filterAccountSizeValue){
                return r.Region__c === this.regionFilterValue && r.vertical_bundle__c === this.filterVBundle && r.EO_Account_Size__c === this.filterAccountSizeValue;
            }
            else if(this.regionFilterValue && this.filterVBundle && !this.filterAccountSizeValue){
                return r.Region__c === this.regionFilterValue && r.vertical_bundle__c === this.filterVBundle;
            }
            else if(this.regionFilterValue && !this.filterVBundle && this.filterAccountSizeValue){
                return r.Region__c === this.regionFilterValue && r.EO_Account_Size__c === this.filterAccountSizeValue;
            }
            else if(!this.regionFilterValue && this.filterVBundle && this.filterAccountSizeValue){
                return r.vertical_bundle__c === this.filterVBundle && r.EO_Account_Size__c === this.filterAccountSizeValue;
            }
            else if(this.regionFilterValue && !this.filterVBundle && !this.filterAccountSizeValue){
                return r.Region__c === this.regionFilterValue;
            }
            else if(!this.regionFilterValue && this.filterVBundle && !this.filterAccountSizeValue){
                return r.vertical_bundle__c === this.filterVBundle;
            }
            else if(!this.regionFilterValue && !this.filterVBundle && this.filterAccountSizeValue){
                return r.EO_Account_Size__c === this.filterAccountSizeValue;
            }
        }, this);

Now two more filters are wanted, so the combinations get a little too many.

2 Answers 2

6

You don't need to handle the combinations directly, instead, filter the list once per test, but allow all records if the test criteria is missing. This is easier to read and more compact if you use Arrow functions instead of traditional functions. You can also chain the filter calls to keep things more readable. Here is your current logic, rewritten:

this.filteredList = this.constList
            .filter( r => !this.regionFilterValue || r.Region__c == this.regionFilterValue)
            .filter( r => !this.filterVBundle || r.vertical_bundle__c == this.filterVBundle)
            .filter( r => !this.filterAccountSizeValue || r.EO_Account_Size__c == this.filterAccountSizeValue);

Note that each filter criteria starts with !filterParam, so that if filterParam is null/undefined, the filter will just pass all records to the next filter. As you add additional criteria, just add another .filter() call to the chain.

2
  • +1 but you forgot the this. bits.
    – sfdcfox
    Commented Aug 16, 2021 at 15:13
  • 1
    My old nemsis, this.. Fixed, and thanks. Commented Aug 16, 2021 at 15:15
2

You can do this with Array.every:

let filters = [];
this.regionFilterValue &&
  filters.push((row) => row.Region__c === this.regionFilterValue);
this.filterVBundle &&
  filters.push((row) => row.vertical_bundle__c === this.filterVBundle);
this.filterAccountSizeValue &&
  filters.push((row) => row.EO_Account_Size__c === this.filterAccountSizeValue);
this.filteredList = this.constList.filter((row) =>
  filters.every((filter) => filter.call(this, row))
);

Every returns true if all values are true, otherwise false. We use arrow functions for brevity, and we can use value && method to apply the right-hand-side only if the left-hand-side is truthy (i.e. not zero, null, empty string, or undefined).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .