2010-01-08 248 views
1

我是Rails(和ruby)的新手。遍历一个数组以遍历一个变量的标准方式是什么?Ruby on Rails数组迭代

例如在一个月的总支出,第一个数组:

expenses_this_month = expenses.find :all, 
            :conditions => ['date >= ? and date <= ?', 
             Date.today.beginning_of_month, Date.today.end_of_month] 

我已经是两个途径知道的这样做:

total = 0.0 
for expense in expenses_this_month 
    total += expense.cost 
end 
return total 

或块

total = 0.0 
expenses_this_month.each do |expense| 
    total += expense.cost 
end 
return total 

我m意识到ruby方法中的最后一行将被默认返回,所以必须有更好的书写方式?

回答

3

您正在寻找的Enumerable#inject方法:

expenses_this_month.inject(0.0) {|total, expense| total + expense } 

这种方法(从Smalltalk中借来的)需要传递给它(在这种情况下,0.0)的值,并设置内部变量了这一点。然后它调用该变量的值(如total)和每个连续元素(如expense),并将该变量设置为块返回的任何值(在此情况下为总和与当前元素的总和)。

您可能想要将此计算卸载到数据库,但是,正如kejadlen所示,通过使用#sum方法。

2

一旦返回的数据,使用inject方法:

total = expenses_this_month.inject { |total, expense| total + expense.cost } 

但是,你应该重写查询:

total = expenses.sum(:cost, :conditions => ['date >= ? and date <= ?', 
            Date.today.beginning_of_month, Date.today.end_of_month]) 
+0

谢谢你的建议。我不使用总和,因为成本是模型中的方法(不是数据库中的列),因为它是根据税率计算的。注入看起来像它会适用于我 – Ryan 2010-01-08 03:38:22

1

如果你使用Rails,你可以使用内置sum类方法(假设Expense是类名)。

expenses_this_month = Expense.sum('cost', 
            :conditions => ['date >= ? and date <= ?', 
                Date.today.beginning_of_month, 
                Date.today.end_of_month]) 
4

与Doug建议的一样,inject方法会很有效。但是,通常最好在数据库中做这样的事情。 ActiveRecord为此提供了一个简单的界面。

total = Expenses.sum :cost, :conditions => { 
    :date => (Date.today.beginning_of_month..Date.today.end_of_month) 
} 

请注意,您还可以使用Range对象,而不是SQL插值。

如果由于其他原因而加载所有费用对象,那么注入方法当然没问题。

+0

+ 1显示一个更简洁的方式来传递日期范围。很好的答案! – 2010-01-08 02:13:04

3
expenses_this_month.map(&:cost).sum 

(短,尽管它在内存中的数组不像减少)

expenses_this_month.reduce(BigDecimal.new('0')) { |total, expense| total + expense.cost } 

你需要记住传递一个初始值减少(否则会返回空数组零)和处理金钱时,使用BigDecimal而不是常规浮点数。

+0

我一直在金额的数据库中使用十进制类型,但如果我通过(0.0)的方法(减少/注入),它会默认为浮动? 另外,注入和还原有什么特定的区别? – Ryan 2010-01-10 09:19:18

+0

reduce是注入的别名,尽管我认为它是在Ruby 1.8.7中引入的,所以为了向后兼容,您可能想要使用注入。我个人偏好使用map/reduce而不是collect/inject,因为这个术语用于各种编程语言。如果你传递一个float作为初始值来减少Decimal/BigDecimal数字的总和,那么你将执行一个添加float + BigDecimal的操作,这会导致浮点数。 – psyho 2010-01-11 09:48:56