Building a Dynamic Filter with ES6 JavaScript
As a developer, there will come a time when you need to filter your data based on user-input. Sure, with a few parameters it isn’t very hard to implement the logic needed. But what happens when your user has a LOT of parameters, and those parameters are constantly changing? This is where the beauty of ES6 comes in.
Getting Started: Example Data
For the purposes of this article, we’ll be looking at some made-up housing data. Let’s assume we acquired this data from some API and it returned this JSON data:
Don’t worry about looking through each entry, we’re going to start small for now. Specifically, let’s focus on the fact that each housing entry contains city
, state
, type
, petsAllowed
, and saleType
. We’ll start with these four parameters to show the basic functionality of our filter.
Storing Filter Parameters
Our next step is to actually contain our filter parameters. Regardless of what framework you’re using (ie. React, Node, etc.), the basic structure will be like this:
Note: each key in the filter matches the key in our JSON data. We use arrays as a way to support multiple selections by the user (ie. a user wants to see listings in both Washington and Oregon).
At some point this filter will be populated. How that is implemented is up to your discretion; all that matters is that the filter is modified properly. Let’s assume for this small example that I want to see listings that match the following criteria:
- Located in either Oregon or California
- Is a House
- Pets are allowed
- Is available For Sale
Thus, our filter will now look like this to reflect our desired query:
Alright, now that you can see how our filter may look, we need to build our query and filter the data.
Filtering the Data
Did you notice how the city
key is empty? We need to ensure we can handle cases like this when a key is empty, otherwise we won’t be correctly filtering our data. We need to make a quick helper function to create a query for us:
All this function will do is take a given filter
input and iterate through each of its keys. It ensures that the key is an Array
and that it contains at least one element. If so, it adds this to the query and ultimately returns our properly-formatted filter.
Now that we can generate our filter, let’s actually filter our data with another helper function:
This function utilizes the ES6 filter
function which provides a very handy way of quickly filtering our data. To summarize what this does:
- Goes through each item in
data
- Compares every key in
query
to the item’s keys - If the item doesn’t contain the key OR if the value of the item’s key is not in the query, throw out the item. Otherwise, include the item in the filtered results.
- Returns the filtered data
Now we can put all of this together to show how this example works.
End Result
Here is the final result for building our basic example (but stay tuned for a more complex example):
If we run this, we will see our filtered data!
$ node basicExample.js[
{
"address": "1234 Example Lane",
"city": "Portland",
"state": "OR",
"zip": "97201",
"listPrice": 350000,
"daysActive": 12,
"type": "House",
"yearBuilt": 1908,
"bedrooms": 3,
"bathrooms": 1,
"squareFootage": 1250,
"hoa": false,
"backyard": true,
"ac": false,
"washerDryerInUnit": true,
"petsAllowed": true,
"saleType": "For Sale"
},
{
"address": "9876 Sunset Dr",
"city": "Los Angeles",
"state": "CA",
"zip": "90025",
"listPrice": 2500000,
"daysActive": 9,
"type": "House",
"yearBuilt": 1965,
"bedrooms": 3,
"bathrooms": 2.5,
"squareFootage": 1125,
"hoa": true,
"backyard": true,
"ac": true,
"washerDryerInUnit": true,
"petsAllowed": true,
"saleType": "For Sale"
}
]
$
Of course, this example doesn’t cover some of the other ways data needs to be filtered. Thankfully, we can modify our code a little to reflect this.
Fine-Tuning the Filter
Now let’s say I want to search for listings with the following parameters:
- The listing is an Apartment
- The Apartment is For Rent
- Is at least $1000 a month, but no more than $1800 a month
- Has at least 1 bedroom
- Has a washer and dryer in the unit
We now need to be able to support min and max ranges for some of our keys. Let’s translate these parameters to our filter:
You’ll notice for the listPrice
and bedrooms
keys we’ve added a min
and max
key. Also notice how the max
value for bedrooms
is set to null
. We’ll use this to specify that there is no ceiling on how many bedrooms we want to search for.
Let’s update the query builder to handle this change:
All we are doing now is adding another condition to our logic to check if the key is an Object
.
But now we need to handle these specific cases. By adding a few if
statements in our filterData
function we can specify what to do in each case:
Since there are a lot of new changes, I’ll just summarize what was changed:
- Added an array called
keysWithMinMax
. This will store all of the keys we are using in the filter that may contain amin
ormax
key (likesquareFootage
oryearBuilt
). - Extracted the condition if the item doesn’t contain the key to its own
if
condition. - Added a new logic for testing our
keysWithMinMax
. Remember how we specifiednull
would be used to indicate that we don’t need to check for these values? That’s how the filter will check whether or not the value needs to be compared. Then it ensures the value is within the specified range.
Now that we’ve updated this, let’s update our script to reflect these changes. I’ll include the old query so you can see the updated results:
Let’s run this to see the results:
$ node advancedExample.js
[
{
"address": "1234 Example Lane",
"city": "Portland",
"state": "OR",
"zip": "97201",
"listPrice": 350000,
"daysActive": 12,
"type": "House",
"yearBuilt": 1908,
"bedrooms": 3,
"bathrooms": 1,
"squareFootage": 1250,
"hoa": false,
"backyard": true,
"ac": false,
"washerDryerInUnit": true,
"petsAllowed": true,
"saleType": "For Sale"
},
{
"address": "9876 Sunset Dr",
"city": "Los Angeles",
"state": "CA",
"zip": "90025",
"listPrice": 2500000,
"daysActive": 9,
"type": "House",
"yearBuilt": 1965,
"bedrooms": 3,
"bathrooms": 2.5,
"squareFootage": 1125,
"hoa": true,
"backyard": true,
"ac": true,
"washerDryerInUnit": true,
"petsAllowed": true,
"saleType": "For Sale"
}
]
----------------------
[
{
"address": "9999 SW 4th Ave",
"city": "Portland",
"state": "OR",
"zip": "97201",
"listPrice": 1200,
"daysActive": 3,
"type": "Apartment",
"yearBuilt": 2002,
"bedrooms": 1,
"bathrooms": 1,
"squareFootage": 655,
"hoa": false,
"backyard": false,
"ac": false,
"washerDryerInUnit": true,
"petsAllowed": true,
"saleType": "For Rent"
},
{
"address": "22 N Washington Dr",
"city": "Seattle",
"state": "WA",
"zip": "98109",
"listPrice": 1750,
"daysActive": 45,
"type": "Apartment",
"yearBuilt": 2010,
"bedrooms": 2,
"bathrooms": 1,
"squareFootage": 950,
"hoa": false,
"backyard": false,
"ac": true,
"washerDryerInUnit": true,
"petsAllowed": true,
"saleType": "For Rent"
}
]
$
Perfect! If you compare the results to the .json
file, you’ll see that these results are correct.
Conclusion
With this knowledge you can build a dynamic filter depending on any of the user’s inputs. Plus, you now have a flexible manner of handling the general cases while also specifying detailed parameters. By utilizing JavaScript’s ES6 modules we can also take advantage of the easy-to-use syntax it provides.