简体   繁体   中英

React: apply multiple filters to array

I'm starting with react and I have an array of objects called arrayToFilter I want to apply multiple filters to, ideally when a user changes the filter select options all filters should be applied to the array and the results returned into a filteredResults array to show, I already have each filter function and their constants but I don't know how to apply all the filters one after another...

Here is my code:

    const [ arrayToFilter, setArrayToFilter ] = useState([ /*array of objects to apply filters to*/ ]);
    const [ filteredResults, setFilteredResults] = useState([ /*array of filtered objects*/ ]);
    const [ categoryOptions, setCategoryOptions ] = useState([ 1,2,3 ]);
    const [ selectedCategoryOptions, setSelectedCategoryOptions ] = useState([ ]);
    const [ searchName, setSearchName ] = useState('');
    const [ hideVenuesWithoutDiscounts, setHideVenuesWithoutDiscounts ] = useState(true);

    const filterHideVenuesWithoutDiscounts = () => 
    {
        if(hideVenuesWithoutDiscounts)
        {
            return arrayToFilter.filter(item => item.discounts.length > 0);
        }
        else
        {
            return arrayToFilter;
        }   
    };

    const filterSelectedCategoryOptions = () => 
    {
        return arrayToFilter.filter(item => item.category_id.includes(selectedCategoryOptions));
    };

    const filtersearchName = () => 
    {
        return arrayToFilter.filter(item =>  item.name.toLowerCase().indexOf(searchTerm.toLowerCase()) != -1);
    };

    useEffect(() => 
    {
        //Filter options updated so apply all filters here
    },
    [searchName, hideVenuesWithoutDiscounts, selectedCategoryOptions]);

Notice useEffect, afaik this is like watch property in vue, I'd like to fire all the filters when a filter input changes

It's simple, you can pass the filtered array as a parameter to the filtering functions so that they can filter the filtered array.

For example:

const filterHideVenuesWithoutDiscounts = (array) => {
    if (hideVenuesWithoutDiscounts) {
        return array.filter((item) => item.discounts.length > 0);
    } else {
        return array;
    }
};

const filterSelectedCategoryOptions = (array) => {
    return array.filter((item) => item.category_id.includes(selectedCategoryOptions));
};

const filtersearchName = (array) => {
    return array.filter((item) => item.name.toLowerCase().indexOf(searchTerm.toLowerCase()) != -1);
};

useEffect(() => {
    //Filter options updated so apply all filters here
    let result = arrayToFilter;
    result = filterHideVenuesWithoutDiscounts(result);
    result = filterSelectedCategoryOptions(result);
    result = filtersearchName(result);
    setArrayToFilter(result);
}, [searchName, hideVenuesWithoutDiscounts, selectedCategoryOptions]);

You could chain multiple filters together in the array.

arrayToFilter
.filter(item => item.category_id.includes(selectedCategoryOptions))
.filter(item => item.discounts.length > 0)
.filter((item) => item.name.toLowerCase().indexOf(searchTerm.toLowerCase()) != -1)

May I suggest one more approach, using Array.reduce , as chaining Array.filters will go over whole array every time that way which can be resource expensive if arrays are huge.

Example:

const filterMethods = [
  (item => item.category_id.includes(selectedCategoryOptions)),
  (item => item.discounts.length > 0),
  ((item) => item.name.toLowerCase().indexOf(searchTerm.toLowerCase()) != -1)
]

const filteredArray = arrayToFilter.reduce((accumulator, currentItem) => {
  for (let i = 0; i < filterMethods.length; i++) {
    if (!filterMethods[i](currentItem)) {
      return accumulator
    }
  }
  return [...accumulator, currentItem]
}, [])

EDIT: After sleeping a while, I noticed that you can actually use Array.filter in similar way as reduce, without going multiple times over the array.

const filterMethods = [
  (item => item.category_id.includes(selectedCategoryOptions)),
  (item => item.discounts.length > 0),
  ((item) => item.name.toLowerCase().indexOf(searchTerm.toLowerCase()) != -1)
]

const filteredArray = arrayToFilter.filter((item) => {
  for (let i = 0; i < filterMethods.length; i++) {
    if (!filterMethods[i](item)) {
      return false
    }
  }
  return true
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM