2015-07-10 87 views
3

我有一個表,每行增加值(下面代碼中的年份)。 我有一個目標,指定一個「閾值」。目標是用戶定義的,它可以包含表格的一個或多個列的值。這意味着你永遠不會知道目標中指定了多少列。 我想匹配表中的第一行,其中行中的值大於目標中的值。我目前有這樣的:根據用戶定義的輸入構建動態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,必須有更優雅的方式。我也想盡可能地減少這段代碼的複雜性。 我如何重寫這段代碼以滿足這些要求?任何建議表示讚賞。

回答

1

爲了避免使用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值(即不falsenil)。


至於支持或邏輯,如果你是快樂的,只是支持或運算所有的條件(例如,使用「或」替代「和」),那麼這將做到這一點:

does_match = conditions.any? {|c| c.call(row) } 

但如果你想支持ORing和ANDing,你需要將它們組合在一起,這更加複雜。

+0

感謝這一點,它實現了所有要求和更多。我會試一試。 –