This is a React.js filter problem and I'm stuck here. The problem has static data of movies as an array of objects and we have to filter that data according to the checked state of different checkboxes.
Static Data:
export const data = [
{
id: 1,
title: 'Batman',
rating: 1,
genre: 'Action'
},
{
id: 2,
title: 'Superman',
rating: 5,
genre: 'Comedy'
},
{
id: 3,
title: 'Spiderman',
rating: 3,
genre: 'Thriller'
},
{
id: 4,
title: 'BananaMan',
rating: 2,
genre: 'Action'
},
{
id: 5,
title: 'OrangeMan',
rating: 4,
genre: 'Drama'
}
];
The abstract UI has the following picture that represents 2 types of filters.
Screenshot:

Both Filters are rendered on the UI as checkboxes. So, I've created a component called Checkboxes which will take a prop named list and it is an array of objects.
This list represents unique ids of checkboxes which will help us to track the checked or unchecked state of the checkbox. The different checkboxes id will save inside the state which is an array.
const [checkedArray, setCheckedArray] = useState([]);
Finally, I'm rendering this Checkboxes component twice inside the parent component called App.js. One for rating and the other for genre.
List:
export const listCheckboxesRating = [
{
id: 0,
name: 'rating',
label: 'Any Rating'
},
{
id: 1,
name: 'rating1',
label: 'Rating 1'
},
{
id: 2,
name: 'rating2',
label: 'Rating 2'
},
{
id: 3,
name: 'rating3',
label: 'Rating 3'
},
{
id: 4,
name: 'rating4',
label: 'Rating 4'
},
{
id: 5,
name: 'rating5',
label: 'Rating 5'
}
];
export const listCheckboxesGenre = [
{
id: 0,
name: 'genre',
label: 'Any Genre'
},
{
id: 1,
name: 'action',
label: 'Action'
},
{
id: 2,
name: 'comedy',
label: 'Comedy'
},
{
id: 3,
name: 'drama',
label: 'Drama'
},
{
id: 4,
name: 'thriller',
label: 'Thriller'
}
];
Checkboxes component:
import {useState} from 'react';
import {handleToggle} from '../../utils';
const Checkboxes = ({list, handleFilters}) => {
const [checkedArray, setCheckedArray] = useState([]);
const onChangeHandler = (checkboxId) => {
const newState = handleToggle(checkboxId, checkedArray);
setCheckedArray(newState);
// Update this checked information into Parent Component
handleFilters(newState);
};
return list.map((item, index) => {
return (
<div key={index}>
<input
type="checkbox"
id={item.name}
checked={checkedArray.indexOf(item.id) !== -1}
onChange={() => onChangeHandler(item.id)}
/>
<label htmlFor={item.name}>{item.label}</label>
</div>
);
});
};
export default Checkboxes;
I'm passing the whole state of these checkboxes through a function named handleFilters to parent component App.js. You can see this line of code.
handleFilters(newState);
The App.js component is saving states of these checkboxes with something like this:
const [selected, setSelected] = useState({
rating: [],
genre: []
});
App.js component:
import {useState} from 'react';
import Checkboxes from './Checkboxes';
import {data, listCheckboxesRating, listCheckboxesGenre} from '../data';
const App = () => {
const [movies, setMovies] = useState(data);
const [selected, setSelected] = useState({
rating: [],
genre: []
});
/**
* This function will perform the filtration process based on the key value.
*
* @param {number[]} checkboxState - It will take the final state of checkboxes
* @param {string} key - It will help us to determine the type of filtration
*/
const handleFilters = (checkboxState, key) => {
const newFilters = {...selected};
newFilters[key] = checkboxState;
// Filtration process
for (let key in newFilters) {
if (
newFilters.hasOwnProperty(key) &&
Array.isArray(newFilters[key]) &&
newFilters[key].length > 0
) {
if (key === 'rating') {
} else if (key === 'genre') {
}
}
}
// Save the filtered movies and update the state.
// setMovies();
setSelected(newFilters);
};
return (
<div>
{movies.map((movie) => (
<div key={movie.id}>
<div>Name: {movie.title}</div>
<div>Genre :{movie.genre}</div>
<div>Rating: {movie.rating}</div>
<hr />
</div>
))}
<div className="row">
<div className="col">
<h1>Filter by Rating</h1>
<Checkboxes
list={listCheckboxesRating}
handleFilters={(checkboxState) =>
handleFilters(checkboxState, 'rating')
}
/>
</div>
<div className="col">
<h1>Filter by Genre</h1>
<Checkboxes
list={listCheckboxesGenre}
handleFilters={(checkboxState) =>
handleFilters(checkboxState, 'genre')
}
/>
</div>
</div>
</div>
);
};
export default App;
The checkboxes checked or unchecked state of both filters are working fine and correctly updated with ids inside the parent selected state with something like this.
const [selected, setSelected] = useState({
rating: [1,2,3],
genre: [2,4]
});
But here is the confusing part where I'm stuck. How to map these checkboxes ids and filter the data according to these ids?
I've defined a function in the App.js component handleFilters(checkboxState, key) in which I wanted to iterate the selected state to perform the filtration process but my mind is not making logic and I need help.
WORKING DEMO:
repository link.
One can use filter() function in JavaScript to filter the object array based on attributes. The filter() function will return a new array containing all the array elements that pass the given condition. If no elements pass the condition it returns an empty array.
Clicking the checkbox next to a value will add a filter for that value and its associated category. If possible, values should be accompanied by a number in parentheses that represents the number of items in the set that match that value.
The idea of filtering with checkboxes means that you have discrete values associated with the checkboxes, when checked, and you want to compare those checked values against properties within an array of objects. Sounds straight-forward, right? The first hurdle is handling checkboxes in Angular.
Filter an Array of Objects in JavaScript. JavaScript arrays have a filter () method that let you create a new array containing only elements that pass a certain test. In other words, filter () gives you a new array containing just the elements you need.
Angular’s custom Pipes can, really, achieve nearly any filtering you’d want. The idea of filtering with checkboxes means that you have discrete values associated with the checkboxes, when checked, and you want to compare those checked values against properties within an array of objects. Sounds straight-forward, right?
The filterObjsinArr function takes in an array of objects: arr and a selection array as arguments. It maps through arr to get individual objects. For each object, it loops through each property, checking if it exists in the selection array. This second method is shorter in terms of lines of code.
So I had to do a couple changes in your code in order to address for proper filtering:
value key on both listCheckboxesRating and listCheckboxesGenre to use it for filtering.export const listCheckboxesGenre = [
{
id: 0,
name: 'genre',
label: 'Any Genre',
value: ''
},
...
}
onChangeHandler to pass value back to handleFilters instead of the id
const onChangeHandler = (checkboxId) => {
// ...
handleFilters(newState.map((id) => list[id].value));
};
const handleFilters = (checkboxState, key) => {
const newFilters = { ...selected };
newFilters[key] = checkboxState;
const hasFilters =
newFilters.rating.length > 0 || newFilters.genre.length > 0;
const filterRating = (module) =>
newFilters.rating.includes(0) ||
newFilters.rating.includes(module.rating);
const filterGenre = (module) =>
newFilters.genre.includes('') || newFilters.genre.includes(module.genre);
const filteredMovies = data.filter(
(m) => !hasFilters || filterRating(m) || filterGenre(m)
);
setMovies(filteredMovies);
setSelected(newFilters);
};
WORKING DEMO:
I did some refactoring.
export const listCheckboxesRating = {
key: 'rating',
data: [
{
id: 1,
name: 'rating1',
label: 'Rating 1',
value: 1
},
...
}
<Checkboxes
list={listCheckboxesGenre}
handleFilters={handleFilters}
/>
// Filtration process
let filteredMovies = data.filter((movie) => {
for (let key in newFilters) {
if (
newFilters.hasOwnProperty(key) &&
Array.isArray(newFilters[key]) &&
newFilters[key].length > 0
) {
if (
newFilters[key].findIndex((value) => movie[key] === value) === -1
) {
return false;
}
}
}
return true;
});
Working DEMO: https://codesandbox.io/s/react-filter-forked-gs2b2?file=/src/components/App.js
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With