I'm trying to create a filter that filters an array of objects by genre and by text.
The desired outcome should be if no filter is passed then everything is displayed, when filtering by text the relevant results are displayed (considering which genre is active) and results by genre are displayed when genre is active considering which text is passed (if any text had been passed)
what happens is that when i try to search by text nothing happens unless i set the filter by genre.
my filter is
async function query(filterBy) {
let melodies = _loadMelodiesFromStorage();
try {
if (!filterBy) return melodies;
if (filterBy.genre === 'none') {
return melodies;
}
const filterRegex = new RegExp(filterBy.text, 'i');
let filteredMelodies = melodies.filter((melody) => {
return filterRegex.test(melody.name);
});
console.log('filteredMelodies:', filteredMelodies);
filteredMelodies = filteredMelodies.filter((melody) => {
return melody.genre === filterBy.genre;
});
return filteredMelodies;
} catch (err) {
console.log('cant get melodies from local storage', err);
throw err;
}
}
this is the filter component :
import React, { useState, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { loadMelodies } from '../cmps/store/melody.action.js';
import { melodyService } from '../service/melody.service.js';
const Filters = () => {
const dispatch = useDispatch();
const genres = melodyService.getGenres();
const [filter, setFilter] = useState({
text: null,
genre: 'none',
});
useEffect(() => {
dispatch(loadMelodies(filter));
}, [filter]);
return (
<div className='w-full flex pb-5'>
<input
className='block appearance-none border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline'
type='text'
placeholder='Search...'
value={filter.text}
onChange={(e) => setFilter({ ...filter, text: e.target.value })}
/>
<select
className='block appearance-none border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline'
value={filter.genre}
onChange={(e) => setFilter({ ...filter, genre: e.target.value })}>
{genres.map((genre) => (
<option value={genre.toLowerCase()}>{genre}</option>
))}
</select>
</div>
);
};
export default Filters;
CodePudding user response:
Does this condition not mean that if your genre
filter is set to 'none'
then your query
function will return all results not having the chance of executing the text filter?
I suggest you separate your query
function into several smaller ones.
if (filterBy.genre === 'none') {
return melodies;
}
CodePudding user response:
I've made it work by adding
if (!filterBy || (filterBy.text === '' && filterBy.genre === 'None')) {
return melodies;
}
might not be the best solution but it works.
CodePudding user response:
I would try to simplify it a bit, maybe something like this:
const query = ({ genre, text } = {}) => {
let list = melodies //get them somewhat
if (genre) {
list = list.filter(m => m.genre === genre)
}
if (text) {
list = list.filter(m => m.text.includes(text))
}
return list
}
const melodies = [
{ genre: 'g1', text: 'this is my text' },
{ genre: 'g2', text: 'another text' },
{ genre: 'g2', text: 'description' }
]
const query = ({ genre, text } = {}) => {
let list = melodies
if (genre) {
list = list.filter(m => m.genre === genre)
}
if (text) {
list = list.filter(m => m.text.includes(text))
}
return list
}
console.log(query({ text: 'text' }))
console.log(query({ genre: 'g2' }))
console.log(query({ genre: 'g2', text: 'description' }))
console.log(query())