我有五个下拉菜单,我需要为每个下拉列表设置条件。我的代码是:
def search(search, compare, year, rain_fall_type)
if search == 'All'
if rain_fall_type == 'All'
all
else
if year == 'All'
if rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
# all
where(Sector: rain_fall_type).order('id')
end
else
if rain_fall_type == "All"
order("#{year} ")
elsif rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
where(Sector: rain_fall_type).order("#{year} ")
end
end
# where(Year: year).order("#{rain_fall_type} ")
end
elsif compare != "None"
if year == 'All'
where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
else
where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
end
else
if rain_fall_type == 'All'
all.order('id')
else
if year == 'All'
if rain_fall_type == "None"
where('Sector = ? ', search).order('id')
else
where('Sector = ? ', rain_fall_type).order('id')
end
else
if rain_fall_type == "None"
if search == "All"
where('Sector = ? ', search).order('id')
else
where('Sector = ? ', search).order('id')
end
else
# all
where('Sector = ? ', rain_fall_type).order('id')
end
end
end
end
end
它有许多if
和else
。我正在努力使条件最小化。缩减此代码的最佳方法是什么?有人建议我改用switch
case
。我应该使用它吗?如果可以,怎么办?
答案 0 :(得分:2)
您可以使用基本为return something if some_condition?
的保护声明。这仅在特定情况下(条件之一是执行单个语句)才可行:
错误的例子:
if condition?
do_something
else
do_something_else
end
这可以写为:
return do_something if condition?
do_something_else
这将减少代码分支。
另外,另一建议是调用具有更多条件的另一种方法,而不是在单个镜头中嵌套条件。
错误的例子:
if condition?
if condition_two?
do_something_two
else
do_something
end
else
do_something_else
end
这可以写为:
if condition?
call_another_method
else
do_something_else
end
def call_another_method
if condition_two?
do_something_two
else
do_something
end
end
您的代码中的示例可能是:
if rain_fall_type == 'All'
all
else
if year == 'All'
if rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
# all
where(Sector: rain_fall_type).order('id')
end
else
if rain_fall_type == "All"
order("#{year} ")
elsif rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
where(Sector: rain_fall_type).order("#{year} ")
end
end
end
可以将其转换为:
return all if rain_fall_type == 'All'
if year == 'All'
return where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id') if rain_fall_type == "None"
where(Sector: rain_fall_type).order('id')
else
return order("#{year} ") if rain_fall_type == "All"
return where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id') if rain_fall_type == "None"
where(Sector: rain_fall_type).order("#{year} ")
end
我希望这会有所帮助:)
注意::这是为了回答How to simplify big conditions?
的原始问题。但是最初的帖子并没有遵循Rails / Ruby的方式进行搜索和过滤,也没有充分利用范围。
答案 1 :(得分:2)
这可能是最好的设置说明。
class Product < ActiveRecord::Base
# custom_scope_1
scope :status, -> (status) { where status: status }
# custom_scope_2
scope :location, -> (location_id) { where location_id: location_id }
# custom_scope_3
scope :search, -> (name) { where("name like ?", "#{name}%")}
end
def index
@products = Product.where(nil) # creates an anonymous scope
@products = @products.status(params[:status]) if params[:status].present?
@products = @products.location(params[:location]) if params[:location].present?
@products = @products.search(params[:search]) if params[:search].present?
end
这可以通过...进一步清理。
def index
@products = Product.where(nil)
filtering_params(params).each do |key, value|
@products = @products.public_send(key, value) if value.present?
end
end
private
# A list of the param names that can be used for filtering the Products
def filtering_params(params)
params.slice(:status, :location, :search)
end
此方法使用ruby元编程来遍历参数并在模型上动态调用预定义的scopes
您可以将此代码移动到模块中,并将其包含在任何支持过滤的模型中
app/models/concerns/filterable.rb
module Filterable
extend ActiveSupport::Concern
module ClassMethods
def filter(filtering_params)
results = self.where(nil)
filtering_params.each do |key, value|
results = results.public_send(key, value) if value.present?
end
results
end
end
end
app/models/product.rb
class Product
include Filterable
...
end
app/controllers/product_controller.rb
def index
@products = Product.filter(params.slice(:status, :location, :search))
end
您现在可以使用控制器中的一行和模型中的一行来过滤和搜索模型
答案 2 :(得分:1)
首先,您的某些逻辑没有意义:
def search(search, compare, year, rain_fall_type)
if search == 'All'
if rain_fall_type == 'All'
all
else
# rain_fall_type != 'All'
if year == 'All'
if rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
where(Sector: rain_fall_type).order('id')
end
else
# in rain_fall_type != 'All' branch, so meaningless 'if'
if rain_fall_type == "All"
order("#{year} ")
elsif rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
where(Sector: rain_fall_type).order("#{year} ")
end
end
end
elsif compare != "None"
# both are same, so meaningless 'if'
if year == 'All'
where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
else
where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
end
else
# search != 'All'
if rain_fall_type == 'All'
all.order('id')
else
if year == 'All'
if rain_fall_type == "None"
where('Sector = ? ', search).order('id')
else
where('Sector = ? ', rain_fall_type).order('id')
end
else
if rain_fall_type == "None"
# in search != 'All' branch, so meaningless 'if'
# AND both are same, so again meaningless 'if'
if search == "All"
where('Sector = ? ', search).order('id')
else
where('Sector = ? ', search).order('id')
end
else
where('Sector = ? ', rain_fall_type).order('id')
end
end
end
end
end
还有更多类似的东西,我不会指出全部,因为无论如何我们都会扔掉所有if
的东西。
最终,我们将查询推迟到方法的结尾,如下所示:
def search(search, compare, year, rain_fall_type)
...
@query = all
@query = @query.where(Sector: @sectors) if @sectors
@query = @query.order(@order) if @order
@query
end
这样,您将where
和order
语句的 all 全部取出,最后只做一次。这样可以节省大量的输入。请参阅muistooshort的评论,以了解(Sector: @sectors)
为何起作用。
因此,诀窍在于设置@sectors
和@order
。首先,我将输入变量分配给实例变量,因为我喜欢这样(并避免变量@search
和方法search
之间的混淆):
def search(search, compare, year, rain_fall_type)
@search, @compare, @year, @rain_fall_type = search, compare, year, rain_fall_type
...
@query = all
@query = @query.where(Sector: @sectors) if @sectors
@query = @query.order(@order) if @order
@query
end
现在,这个答案已经进行了太长时间了,因此我不会在所有gorey细节中拖拉您。但是,添加几个辅助方法(sectors_to_use
和order_to_use
)并用它们代替@sectors
和@order
,基本上可以得出以下结论:
def search(search, compare, year, rain_fall_type)
@search, @compare, @year, @rain_fall_type = search, compare, year, rain_fall_type
@query = all
@query = @query.where(Sector: sectors_to_use) if sectors_to_use
@query = @query.order(order_to_use) if order_to_use
@query
end
private
def sectors_to_use
return [@rain_fall_type, @compare] if @search != 'All' && @compare != 'None'
unless @rain_fall_type == 'All'
if @rain_fall_type == 'None'
@search == 'All' ? ['Primary', 'Secondary', 'Tertiary'] : [@search]
else
[@rain_fall_type]
end
end
end
def order_to_use
return nil if (@search == 'All') && (@rain_fall_type == 'All')
return @year if (@search == 'All') && !(@year == 'All')
return :id
end
不到代码行的一半,字符减少了上千个,ifs
也减少了很多。