Search code examples
javascriptarraysfilterdynamic-arrays

Dynamic Filtering of Array with Multiple Confitions


I have an array which is constructed using user input values and can have any length:

var products = [
 {key1: string1,
  key2: 1,
  key3: 30,
  key4: 1},

 {key1: string2,
  key2: 2,
  key3: 35,
  key4: 2}
]

And I have some filters

var filters = {
  key2: 1,
  key3: {min: 20, max: 40},
  key4: 1,
}

I'm currently using the below function to filter products using my filters

        function multiFilter(array, filters) {
            const filterKeys = Object.keys(filters);
            return array.filter((item) => {
                // flipped around, and item[key] forced to a string
                return filterKeys.every(key => !!~String(item[key]).indexOf(filters[key]));
            });
        }

        var filtered = multiFilter(products, filters);
        console.log(filtered);

Everything works as expected when key3 in filters has an exact number, but I'm running into issues when trying to incorporate a range. How would I amend the function to allow for a range in key3 (filters)?


Solution

  • Maybe this approach would be useful

    const products = [
     {key1: 'string0',key2: 0,key3: 10,key4: 1},
     {key1: 'string1',key2: 1,key3: 15,key4: 1},
     {key1: 'string2',key2: 2,key3: 20,key4: 1},
     {key1: 'string3',key2: 3,key3: 25,key4: 2},
     {key1: 'string4',key2: 4,key3: 30,key4: 2},
     {key1: 'string5',key2: 5,key3: 35,key4: 2},
     {key1: 'string6',key2: 6,key3: 40,key4: 4},
     {key1: 'string7',key2: 7,key3: 45,key4: 4},
     {key1: 'string8',key2: 8,key3: 50,key4: 4},
     {key1: 'string9',key2: 9,key3: 55,key4: 4}];
     
    const filters = {
      key2: {max: 7},
      key3: {min: 20, max: 60},
      key4: 4,
    };
    
    const filterProdacts = (products, filters) => {
    // First, we create an object where the property values (min, max, eq) 
    // are predicate functions.
      const fns = {
        eq: (key, value) => (obj) => obj[key] === value,
        min: (key, value) => (obj) => obj[key] >= value,
        max: (key, value) => (obj) => obj[key] <= value,
      };
    
    // Generates a predicate based on the conditions
      makePredicat = (filterKey, filterValue) => {
    
    // For consistency мake the numeric values as an object like { eq: 5 }
        const filterObj = (typeof filterValue === 'object') 
          ? filterValue
          : { eq: filterValue };
          
    // Pick a function from fns and initialize it with the condition parameters
        return Object.entries(filterObj)
          .map(([condKey, condValue]) => fns[condKey](filterKey, condValue));
      };
      
    // Fill in the array with predicate functions for each condition. 
    // In this example there will be 4 functions, one for each condition 
    // key2 - max: 7; key3 - min: 20; key3 - max: 60; key4 - eq: 4;
    const predicats = Object.entries(filters).flatMap(([key, value]) => makePredicat(key, value));
    
    // Initializing reduce by source array 
    // then filtering it with all predicate functions
    return predicats.reduce((acc, fn) => acc.filter(fn), products);
    };
    
    console.log(filterProdacts(products, filters));
    .as-console-wrapper{min-height: 100%!important; top: 0}