The most elegant way of applying several filters in Ruby #select

67 Views Asked by At

I have a query object in my application that filters data with Array#select method

class QueryObject
  def call(filters)
    data = get_data # returns array 
    data = by_param1(data, filter[:param1])
    data = by_param2(data, filter[:param2])
  end

  private

  def by_param1(data, filter)
    data.select { |d| #filtering goes here }
  end

  def by_param2(data, filter)
    data.select { |d| #filtering goes here }
  end
end

How could I pass my filtering blocks to Enumerable to apply all filters and chain them?
I know that I can do something like that:

data.select { |d| by_param1 && by_param2 }

but it's not elegant in my case because I have a lot of filters

1

There are 1 best solutions below

1
Mike Challis On

You can use #reduce to great effect here.

For example,

filter_1 = -> (thing) { thing > 2 }

filter_2 = -> (thing) { thing < 5 }

def better_filter(input)
  input * 7 < 23
end

filters = [filter_1, filter_2, method(:better_filter), :positive?]

array = (0..9).to_a

filters.reduce(array) { |result, filter| result.select(&filter) }

# [3]

In the context of your question, it could go like this:

class QueryObject
  def call(filters)
    data = get_data # returns array 
    filters.reduce(data) { |result, filter| result.select(&filter) }
  end
end

This assumes that filters is passed in as an array of objects that respond to #call. That could be a lambda, a simple ruby class, or even a symbol, as I showed in the example.