Advanced filtering in React

Hey guys!So I’m making a pretty robust app currently, and I’m pretty worried that the filtering logic I’m using isn’t the best. I’m wondering how you guys handle more advanced filtering. Now I’m not talking about something like this:

const filtered = array.filter((data) => data === condition);

I’m talking about a big data set with a lot of options to filter.
Now let’s take movies for example. Let’s say you’re building a website where users can see a list of movies and then filter it to their liking. A movie array would look something like this:

const movies = [
    {
title: 'Nomadland',
year: 2020,
genre: 'drama',
producers: [ 'Frances', 'Chloe' ],
rating: 7.4,
description: '...'
    },
    {
title: 'The Irishman',
year: 2019,
genre: 'Crime/Drama',
producers: [ 'Robert De Niro', 'MAartin Scorsese' ],
rating: 7.8,
description: '...'
    }
];

etc… You get the gist.

So now you include a filter that looks something like this:

<Select name="genre">
<Option value="drama">Drama</Option>
<Option value="drama">Horror</Option>
<Option value="drama">Comedy</Option>
</Select>
<Select name="year">
<Option value="drama">2020</Option>
<Option value="drama">2019</Option>
<Option value="drama">2018</Option>
</Select>

Now, what I did, was attach an event listener to selects, that would fire on select change:

<Select onChange={(e, data) => handleFilters('genre', data.map((d) => d.value))}>

Which calls the following function:

const handleFilters = (name, values) => {
const filter = { name, values };
const active = activeFilters.filter((o) => o.name !== name);
const filters = [ ...active, filter ];
const qsd = filters.forEach((filter, index) => {
if (filter.values.length > 0) {
if (filter.values.length === 1) {
return {
_where: filter.values.map((value) => ({
[filter.name]: value
                        }))
                    };
                } else {
return {
_where: filter.values.map((value) => ({
_or: [ { [filter.name]: value } ]
                        }))
                    };
                }
            }
        });
setActiveFilters(filters);
    };

(oof formatting got f’d up, so maybe try and paste this into your code editor for better visibility)

Anyways, what this function tries to accomplish, is to have a more generic approach to building filters that can be sent to the server (as query strings). I’m completely trying to avoid manually declaring filters and manually setting each filter, because that would be excruciating if you had a filter for every property (which I’m trying to do). But I still feel like I’m doing it wrong.
The above function doesn’t work as expected, but I think you can figure out what I was trying to accomplish.

I would appreciate some articles, videos or advices on how to build a better filter functionality that is maintainable, readable and scalable, no matter if you want to apply 1 or 100 filters.

Imma go and try to fix this function until someone (hopefully) gives me a better advice on how to fix this.

First thought:
Have a single filter object, that you compare to the movies, rather than an array of filter objects.

Second thought: How do you turn a filter off, in that HTML?

Assuming your HTML is something more like:

<form id="FilterForm">
<Select name="genre">
<option value="">Any</option>
<Option value="drama">Drama</Option>
<Option value="horror">Horror</Option>
<Option value="comedy">Comedy</Option>
</Select>
<Select name="year">
<option value="">Any</option>
<Option value="2020">2020</Option>
<Option value="2019">2019</Option>
<Option value="2018">2018</Option>
</Select>
</form>

Your filter object at any given time is

let filters = Object.fromEntries([...(new FormData(document.forms.FilterForm))]);

Here’s how I would do it.

function filterStuff() {
  let filters = Object.fromEntries([...new FormData(document.forms.FilterForm)]);
  return movies.filter((mov) => {
    for (const key in filters) {
      if (filters[key] != "" && mov[key] != filters[key]) {
        return false;
      }
    }
    return true;
  });
}

This, of course, is a pure Javascript filtering. React may not expose things the same way. Also, “Crime/Drama” does not work for this simple filter check. You’d have to do something like includes.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.