S.ork
S.ork

Reputation: 229

Setting multiple attributes of a Ruby object in a loop

I have a class with bunch of attributes (first, second, third, etc.). I need to go through all "even" attributes (i.e. second, fourth, sixth, etc.) and make some corrections to just those:

report.first = Calculate('1')

report.second = Calculate('2')
report.second *= 0.9 if requireCorrection && report.second > 5

report.third = Calculate('3')

report.fourth = Calculate('4')
report.fourth *= 0.9 if requireCorrection && report.fourth > 5

report.fifth = Calculate('5')

report.sixth = Calculate('6')
report.sixth *= 0.9 if requireCorrection && report.sixth > 5

# etc.

As you can see, I have the exact same code for each "even" attribute, except for the different attribute name. Is there a way to avoid this repetition in the code (I have around 50 attributes in total)?

Upvotes: 1

Views: 418

Answers (1)

Mate Solymosi
Mate Solymosi

Reputation: 5977

You can use Object#send to invoke an arbitrary method on report by specifying the method name as a string or a symbol:

def make_correction(report, which_method)
  current_value = report.send(which_method)
  current_value *= 0.9 if current_value > 5
  report.send "#{which_method}=", current_value
end

The method names first, second, third, etc. do not easily lend themselves to automatization, but if you renamed the methods to item_1, item_2, etc., you could use a loop to process all of them:

(1..50).each do |i|
  report.send "item_#{i}=", Calculate(i.to_s)

  if i.even?
    make_correction(report, "item_#{i}") if require_correction
  end
end

If the method names need to stay the same, you can still simplify the code like this:

attrs = [:first, :second, :third, :fourth, :fifth]

attrs.each_with_index do |attr, ix|
  i = ix + 1  # indexes are zero based, we need to adjust them

  report.send "#{attr}=", Calculate(i.to_s)

  if i.even?
    make_correction(report, attr) if require_correction
  end
end

Upvotes: 1

Related Questions