Ruby で複数のフィルターを適用する最もエレガントな方法 #select
概要
アプリケーションに Array#select メソッドでデータをフィルタリングするクエリ オブジェクトがあります
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
すべてのフィルターを適用してチェーンするには、フィルター ブロックを Enumerable に渡すにはどうすればよいでしょうか? 私はそのようなことができることを知っています:
data.select { |d| by_param1 && by_param2 }
でも私の場合はフィルターがたくさんあるのでエレガントではありません
解決策
ここで #reduce を使用すると、大きな効果が得られます。
例えば、
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]
あなたの質問の文脈では、次のようになります。
class QueryObject
def call(filters)
data = get_data # returns array
filters.reduce(data) { |result, filter| result.select(&filter) }
end
end
これは、フィルターが #call に応答するオブジェクトの配列として渡されることを前提としています。例で示したように、それはラムダ、単純な Ruby クラス、さらにはシンボルである可能性があります。