如何在一行中迭代一个 ActiveRecord 结果集,并且没有检查 Ruby

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

我有一个 Active Record 结果数组,我想遍历每条记录以获取特定属性并将所有属性添加到一行中并进行 nil 检查。这是我到目前为止得到的

def total_cost(cost_rec)
    total= 0.0
    unless cost_rec.nil?
      cost_rec.each { |c| total += c.cost }
    end
    total
  end

有没有一种优雅的方法可以在一行中做同样的事情?

类似这些吗?

def total_cost(cost_rec)
  (cost_rec || []).inject(0) { |memo, c| memo + c.cost }
end

def total_cost(cost_rec)
  (cost_rec || []).sum(&:cost)
end

其中任何一个都应该有效

total = cost_rec.map(&:cost).compact.sum
total = cost_rec.map{|c| c.cost }.compact.sum
total = cost_rec.pluck(:cost).compact.sum

编辑:如果 cost_rec 为零

total = (cost_rec || []).map{|c| c.cost }.compact.sum

您可以结合安全导航(“隐藏”nil 检查)、数据库内求和(避免从数据库中提取一堆您不需要的数据),以及#to_f 隐藏最终 nil 检查的调用:

cost_rec&.sum(:cost).to_f

如果 cost 是整数,则:

cost_rec&.sum(:cost).to_i

如果 cost 是数据库中的 numeric 并且您不想担心精度问题:

cost_rec&.sum(:cost).to_d

如果 cost_rec 是数组而不是关系(即您已经将所有数据从数据库中提取出来),则以下之一:

cost_rec&.sum(&:cost).to_f
cost_rec&.sum(&:cost).to_i
cost_rec&.sum(&:cost).to_d

取决于 cost 是什么类型。

你也可以使用Kernel#Array来忽略nils(因为Array(nil)[])并且忽略数组和ActiveRecord关系之间的区别(因为#Array 调用 #to_ary 并且关系响应)并说:

Array(cost_rec).sum(&:cost)

这甚至允许 cost_rec 成为单个模型实例。这也绕过了对最终 #to_X 调用的需要,因为 [].sum0。这种方法的缺点是当 cost_rec 是一个关系时,您不能将求和推入数据库。

cost_recActiveRecord::Relatation 时,这应该是开箱即用的:

cost_rec.sum(:cost)

参见 ActiveRecord::Calculations#sum