DEV Community

Cover image for How to filter an Array of Objects in React?
collegewap
collegewap

Posted on • Updated on • Originally published at codingdeft.com

How to filter an Array of Objects in React?

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
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Here we have a list of 10 employees and we are looping through them
and displaying each employee's information on a card.

list of employees

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
    })
  )
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Source code and demo

You can download the source code here and view a demo here.

Oldest comments (2)

Collapse
 
richo profile image
Richo

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

Collapse
 
patrick19877 profile image
patrick19877

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..