Search code examples
ruby-on-railsrubyone-liner

How to iterate over an ActiveRecord resultset in one line with nil check in Ruby


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?


Solution

  • 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 nils (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.