Home > Net >  Double filter only filters when one is active
Double filter only filters when one is active

Time:06-23

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())

  • Related