Generic filter for X number of properties

Viewed 33

I want to make a generic filter function. Currently I have a function that looks like this:

 const filterRows = () => {
    items.filter((item) => {
      if(selectedDrinks.length > 0 && selectIds.length > 0) {
        return selectedDrinks.includes(item.description) && selectedIds.includes(item.props.id)
      }else if(selectedDrinks.length > 0) {
        return selectedDrinks.includes(item.description)
      }else if(selectedIds.length > 0) {
        return selectedIds.includes(item.props.id)
    }
  }
}

The number of if checks I need to do will grow exponentially if I add one more thing to filter by.

I've made a pathetic try below. One issue I encountered is if I have a nested structure and want to access ["props/id"] as I don't know the syntax for it. Also tried ["props:id"] etc. And if I add multiple strings in the query it does not work either. And even if I could add multiple strings properly it would only work as an OR.

And for me it would be selectedDrinks && selectedId as both need to match for it to filter, not selectedDrinks || selectedIds

I want to include everything in both selectedDrinks and selectedIds as a query, and they should filter only if both are included in "assets" as description and props:id. I should also be able to add e.g "selectedNames" as a third "query parameter".

const selectedDrinks: string[] = [
    "cola",
    "fanta",
]

const selectedIds : string[] = [
    "5",
    "4",
]

interface s {
    description: string;
    name: string;
    props: {
        id: string
    }
}


const items: s[] = [

    {
        description: "cola",
        name: "computer",
        props: {
            id: "4"
        }
    },
    {
        description: "fanta",
        name: "laptop",
        props: {
            id: "5"
        }
    },
    {
        description: "sprite",
        name: "phone",
        props: {
            id: "6"
        }
    }
]



export function genericFilter<T>(
    object: T, 
    filters: Array<keyof T>,
    query: string[]
    ):boolean {
    
        if(query.length === 0)
            return true
        return filters.some(filter => {
            const value = object[filter]
            console.log(value)
            
            if(typeof value === "string") {
                return value.toLowerCase().includes(query.map(q => q.toLowerCase()).join(""))
            }
            if(typeof value === "number") {
                return value.toString().includes(query.map(q => q.toLowerCase()).join(""))
            }

            return false
        })  
   
  }


const myFilterResult = items.filter((asset) => genericFilter(item, ["props", "name"], ["5"]))

console.log(myFilterResult)
1 Answers

If anyone is interested, here is how I solved it.

  /**
   *
   * @returns A new list of filtered objects
   * @param objects The objects that we want to filter
   * @param properties The properties we want to apply on the object and compare with the query
   * @param queries The queries we want to filter by
   */

   export function genericFilter<T>(
    objects: T[], 
    properties: Array<keyof T>,
    queries: Array<string>[] | Array<number>[] 
    ):T[] {
        return objects.filter((object) =>  {
            var count = 0;
            properties.some((props) => {
                const objectValue = object[props]
                if(typeof objectValue === "string" || typeof objectValue === "number") { 
                    queries.forEach((query) => {
                        query.forEach((queryValue) => { 
                            if(queryValue === objectValue) {
                                count+=1;
                            }
                        })
                    })   
                }
            })
            return count === properties.length;
        })
  }

export default genericFilter;

How you call the function, can include X amount of filters and strings to search for.

const result = genericFilter(assets, ["description", "id", "name"], [selectedAssetTypes, selectedIds, selectedNames])
Related