I have an array of Active Record result and I want to iterate over each record to get a specific attribute and add all of them in one line with a nil check. Here is what I got so far
def total_cost(cost_rec)
total= 0.0
unless cost_rec.nil?
cost_rec.each { |c| total += c.cost }
end
total
end
Is there an elegant way to do the same thing in one line?
You could combine safe-navigation (to "hide" the nil
check), summation inside the database (to avoid pulling a bunch of data out of the database that you don't need), and a #to_f
call to hide the final nil
check:
cost_rec&.sum(:cost).to_f
If the cost
is an integer, then:
cost_rec&.sum(:cost).to_i
and if cost
is a numeric
inside the database and you don't want to worry about precision issues:
cost_rec&.sum(:cost).to_d
If cost_rec
is an array rather than a relation (i.e. you've already pulled all the data out of the database), then one of:
cost_rec&.sum(&:cost).to_f
cost_rec&.sum(&:cost).to_i
cost_rec&.sum(&:cost).to_d
depending on what type cost
is.
You could also use Kernel#Array
to ignore nil
s (since Array(nil)
is []
) and ignore the difference between arrays and ActiveRecord relations (since #Array
calls #to_ary
and relations respond to that) and say:
Array(cost_rec).sum(&:cost)
that'll even allow cost_rec
to be a single model instance. This also bypasses the need for the final #to_X
call since [].sum
is 0
. The downside of this approach is that you can't push the summation into the database when cost_rec
is a relation.