根据用户定义的输入构建动态if语句
问题描述:
我有一个表,每行增加值(下面代码中的年份)。 我有一个目标,指定一个“阈值”。目标是用户定义的,它可以包含表格的一个或多个列的值。这意味着你永远不会知道目标中指定了多少列。 我想匹配表中的第一行,其中行中的值大于目标中的值。我目前有这样的:根据用户定义的输入构建动态if语句
class Target < ActiveRecord::Base
def loop_sheets(sheets, year_array)
result = nil
elements = self.class.column_names[1..-3].map(&:to_sym)
to_match = elements.select{|e| self.send(e) != nil }
condition = to_match.map do |attr|
"row[:#{attr}] > #{attr}"
end.join " and "
year_array.each do |year|
sheets.each do |sheet|
row = sheet.calculation.datatable.select { |r| r[:year] == year }.first
does_match = eval(condition)
if does_match
result = {
:year => row[:year],
:sheet_name => sheet.name
}
return result
end
end
end
return result
end
end
这很好用,但现在算法固定使用AND匹配。我想支持OR匹配以及AND匹配。另外我想避免使用eval,必须有更优雅的方式。我也想尽可能地减少这段代码的复杂性。 我如何重写这段代码以满足这些要求?任何建议表示赞赏。
答
为了避免使用eval
:Ruby可以动态创建代码,所以可以这样做,而不是将字符串添加到一起。你所要做的就是把琴弦拿走!
conditions = to_match.map do |attr|
proc {|row| row[attr.to_sym] > attr }
end
现在你有拿row
当作它们的参数和返回的条件(不要求return
关键字)的结果可运行块阵列。如果你只是在做and
,它是那样简单:
does_match = conditions.all? {|c| c.call(row) }
这将是true
只有所有条件返回truthy值(即不false
或nil
)。
至于支持或逻辑,如果你是快乐的,只是支持或运算所有的条件(例如,使用“或”替代“和”),那么这将做到这一点:
does_match = conditions.any? {|c| c.call(row) }
但如果你想支持ORing和ANDing,你需要将它们组合在一起,这更加复杂。
感谢这一点,它实现了所有要求和更多。我会试一试。 –