In my previous article, I explained how to filter numbers and strings in React. In this article, we will see how to filter an array of objects.
Project setup
Create a react app using the following command:
npx create-react-app react-filter-object-array
Add the following css to index.css
:
body {
display: flex;
justify-content: center;
}
ul {
list-style-type: none;
padding: 0;
}
li {
margin: 10px 0;
border: 1px solid;
padding: 10px;
}
.experience_filter {
display: flex;
gap: 6px;
margin: 10px 0;
}
.experience_filter button {
cursor: pointer;
border: 1px solid;
border-radius: 4px;
padding: 4px;
background-color: white;
}
.experience_filter button.selected {
background-color: wheat;
}
Update the App.js
with the following code:
import { useState } from "react"
const employees = [
{
id: 1,
name: "Tobe",
experience: 4,
department: "Accounting",
},
{
id: 2,
name: "Jolee",
experience: 13,
department: "Services",
},
{
id: 3,
name: "Muhammad",
experience: 14,
department: "Training",
},
{
id: 4,
name: "Hubie",
experience: 5,
department: "Sales",
},
{
id: 5,
name: "Yoshiko",
experience: 16,
department: "Services",
},
{
id: 6,
name: "Beatrix",
experience: 17,
department: "Human Resources",
},
{
id: 7,
name: "Jacob",
experience: 4,
department: "Engineering",
},
{
id: 8,
name: "Koren",
experience: 4,
department: "Accounting",
},
{
id: 9,
name: "Marissa",
experience: 20,
department: "Support",
},
{
id: 10,
name: "Rufe",
experience: 18,
department: "Training",
},
]
function App() {
const [filteredEmployees, setFilteredEmployees] = useState(employees)
return (
<div className="App">
<ul>
{filteredEmployees.map(employee => {
const { name, experience, department, id } = employee
return (
<li key={id}>
<div>
Name: <strong>{name}</strong>
</div>
<div>Experience: {experience} year(s)</div>
<div>Department: {department}</div>
</li>
)
})}
</ul>
</div>
)
}
export default App
Here we have a list of 10 employees and we are looping through them
and displaying each employee's information on a card.
Filtering based on department
You can filter the employees by department using the following function:
const filterByDepartment = department => {
setFilteredEmployees(
employees.filter(employee => {
return employee.department === department
})
)
}
Here we are using the array filter function, and passing a callback to it. The callback will be called for each item in the employees array. If the callback returns true for an employee, only that employee will be added to the array, which will be returned by the filter function. We are setting the returned array to the filteredEmployees
state.
Now, let's add a dropdown to list unique departments:
import { useState } from "react"
const employees = [
//...
]
function App() {
const [filteredEmployees, setFilteredEmployees] = useState(employees)
const filterByDepartment = department => {
setFilteredEmployees(
employees.filter(employee => employee.department === department)
)
}
// Using Set to filter unique values
const departments = Array.from(
new Set(employees.map(employee => employee.department))
)
return (
<div className="App">
<select onChange={e => filterByDepartment(e.target.value)}>
<option value="" disabled default selected>
Select department
</option>
{departments.map(department => {
return <option key={department}>{department}</option>
})}
</select>
<ul>
{filteredEmployees.map(employee => {
const { name, experience, department, id } = employee
return (
<li key={id}>
<div>
Name: <strong>{name}</strong>
</div>
<div>Experience: {experience} year(s)</div>
<div>Department: {department}</div>
</li>
)
})}
</ul>
</div>
)
}
export default App
Now if you run the application, and select a department, you should see the employees only from that department.
Filtering multiple values
If you need to add another filter, you can store the filter in a different state and run an useEffect hook, whenever the filer changes.
import { useEffect, useState } from "react"
const employees = [
//..
]
function App() {
const [filteredEmployees, setFilteredEmployees] = useState(employees)
const [department, setDepartment] = useState()
const [experience, setExperience] = useState()
// Using Set to filter unique values
const departments = Array.from(
new Set(employees.map(employee => employee.department))
)
useEffect(() => {
setFilteredEmployees(
employees.filter(employee => {
return (
(!department || department === employee.department) &&
(!experience ||
(experience === "LESS_THAN_10"
? employee.experience < 10
: employee.experience >= 10))
)
})
)
}, [department, experience])
return (
<div className="App">
<select onChange={e => setDepartment(e.target.value)}>
<option value="" disabled default selected>
Select department
</option>
{departments.map(department => {
return <option key={department}>{department}</option>
})}
</select>
<div className="experience_filter">
<button
className={`${experience === "LESS_THAN_10" ? "selected" : ""}`}
onClick={() => setExperience("LESS_THAN_10")}
>
Less than 10 years
</button>
<button
className={`${experience === "10_PLUS" ? "selected" : ""}`}
onClick={() => setExperience("10_PLUS")}
>
10+ years
</button>
</div>
<ul>
{filteredEmployees.map(employee => {
const { name, experience, department, id } = employee
return (
<li key={id}>
<div>
Name: <strong>{name}</strong>
</div>
<div>Experience: {experience} year(s)</div>
<div>Department: {department}</div>
</li>
)
})}
{filteredEmployees.length === 0 && (
<div>No employees matching the filter</div>
)}
</ul>
</div>
)
}
export default App
In the above code, we have added an additional filter based on experience.
Each time the user changes a filter, we are saving the filter to its corresponding state. Also, we are running useEffect each time the filter changes.
Inside the useEffect, we are checking if the department filter and experience filter is applied. We have a not check (!department
and !experience
), so that we can apply the filter only if that particular filter is selected.
Clearing all filters
Finally, let's add a clear filter button, to clear all the filters:
import { useEffect, useState } from "react"
const employees = [
//..
]
function App() {
const [filteredEmployees, setFilteredEmployees] = useState(employees)
const [department, setDepartment] = useState()
const [experience, setExperience] = useState()
// Using Set to filter unique values
const departments = Array.from(
new Set(employees.map(employee => employee.department))
)
useEffect(() => {
setFilteredEmployees(
employees.filter(employee => {
return (
(!department || department === employee.department) &&
(!experience ||
(experience === "LESS_THAN_10"
? employee.experience < 10
: employee.experience >= 10))
)
})
)
}, [department, experience])
const clearFilters = () => {
setDepartment()
setExperience()
}
return (
<div className="App">
<select onChange={e => setDepartment(e.target.value)}>
<option value="" disabled default selected>
Select department
</option>
{departments.map(department => {
return <option key={department}>{department}</option>
})}
</select>
<div className="experience_filter">
<button
className={`${experience === "LESS_THAN_10" ? "selected" : ""}`}
onClick={() => setExperience("LESS_THAN_10")}
>
Less than 10 years
</button>
<button
className={`${experience === "10_PLUS" ? "selected" : ""}`}
onClick={() => setExperience("10_PLUS")}
>
10+ years
</button>
</div>
<button onClick={clearFilters}>Clear All filters</button>
<ul>
{filteredEmployees.map(employee => {
const { name, experience, department, id } = employee
return (
<li key={id}>
<div>
Name: <strong>{name}</strong>
</div>
<div>Experience: {experience} year(s)</div>
<div>Department: {department}</div>
</li>
)
})}
{filteredEmployees.length === 0 && (
<div>No employees matching the filter</div>
)}
</ul>
</div>
)
}
export default App
Top comments (2)
Wouldn't it make more sense to define a multi dimensional array and then add items to it as we go?
[
"Accounting": [{
"Tobe": {...}
}],
"Services": [{"Jolee": {...}}]
]
I am trying to do something similar and then filter. so we can jut add the items to the array without having to repeat the department type on each item. they should be added to their own section of the multi array. i'm getting stuck with the filtering part if i do it this way..
Hi, this is exactly what I was looking for, one thing I noticed that when the "Clear All Filter" is clicked, it doesn't reset the "Select department" drop-down. How can I achieve it?
Many thanks