Package for client side filtering?


#1

I’m currently trying to filter an array of objects that I receive reactively from the server. I’m using three select menus to act as filters, see below.

As a user uses these filters I would like to be able to filter through the array of objects and only show the ones that should be showing. As of right now I’m currently using what was given in the todo/react tutorial, I’m using Array.Filter & then .mapping over the filtered results to render the components. My issue is in the filter step, as of right now I haven’t found of a clean and concise way to write my filter logic without using nested if/s which are hard to reason about and error prone. Is there a package or better way I should be handling this?

Here’s a snippet, this is the function that I’m using to feed into the Array.Filter(), i’ve taken the filter logic out for simplicity.

checkFilter(track, genreFilter, priceFilter) {
    if (genreFilter === "all" && priceFilter === "all") {
      return track;
    } else {
      if (genreFilter === track.genre) {
        return track;
      }
    }

#2

Could someone point me in the right direction?


#3

If you can add more code snippet so we can understand your issue (no one will steal it :wink: )


#4

Sure,

    const allTracks      = this.props.tracks; 
    const filteredTracks = allTracks.filter( (track) => {
      let genreFilter    = this.props.genreFilter.genre,
          priceFilter    = this.props.genreFilter.price,
          licenseFilter  = this.props.genreFilter.license;
        
      return this.checkFilter(track, genreFilter, priceFilter);
      });
 
    return filteredTracks.map( (track) => {
      const currentUserId = this.props.currentUser && this.props.currentUser._id;
      const showPrivateButton = track.owner === currentUserId;
      const currentUser = Meteor.user();
      const source = track.fileSource;    
      return (
        <Track 
          key={track._id} 
          song={track}
          source={source}
          showPrivateButton={showPrivateButton}
          currentUser={currentUser}
        />
      );
    });
  }

here is the checkFilter function

 checkFilter(track, genreFilter, priceFilter) {
 
    if (track.genre === genreFilter || genreFilter === "all") {
      if (track.price <= priceFilter || priceFilter === 0) {
        return track;
      } 
    }

with this I am able to successfully filter on the genre & price filter. I’m wondering if this is the best way to do such a thing.


#5

Okay I think I was overthinking things. Using these three nested If statements



    if (track.genre === genreFilter || genreFilter === "all") {
      if (track.price <= priceFilter || priceFilter === 0) {
        if (licenseType === "all" || licenseType === track.licenseType ) {
            return track;
        }
      } 
    }

I was able to get the functionality I’m looking for. I’ll do further testing and see if I can come up with anything to break this logic.


#6

You don’t need to use complicated JS filtering. Just dynamically pass an array of filters to your .find () query on mongo client side.

From Mongo docs:
$and performs a logical AND operation on an array of two or more expressions (e.g. , , etc.) and selects the documents that satisfy all the expressions


#7

@globalise wouldn’t this cause unnecessary re-renders every time a filter is changed? I’d like to minimize trips to the server. As of right now the logic up-top just filters through the mini-mongo collection of tracks that are available on the client. I’ll look into the $and operator though, thank-you.


#8

No server round trips required using the approach @globalise is advocating (which I’d also advocate – e.g. What if you wanted to add more filters?). You have a client side document cache (minimongo), which is the only thing getting queried by a client side .find(). Minimongo uses the same query syntax as mongo, but it’s not the actual remote database you’re hitting with a .find() query on the client.