Ruby on rails array iteration
I'm new to Rails (and ruby). What is the standard way of iterating through an array to total a variable.
e.g. for the total expenses in a month, first an Array:
expenses_this_month = expenses.find :all,
:conditions => ['date >= ? and date <= ?',
Date.today.beginning_of_month, Date.today.end_of_month]
I already of know of two ways of doing it:
total = 0.0
for expense in expenses_this_开发者_JAVA百科month
total += expense.cost
end
return total
or with a block
total = 0.0
expenses_this_month.each do |expense|
total += expense.cost
end
return total
I'm aware that the last line in a ruby method will be returned by default, so there must be a better way of writing this?
The inject
method will work great, like Doug suggested. However, it's generally preferable to do things like this in the database when you can. ActiveRecord provides a simple interface for this.
total = Expenses.sum :cost, :conditions => {
:date => (Date.today.beginning_of_month..Date.today.end_of_month)
}
Note that you can also use a Range object instead of SQL interpolation.
If you're loading all the Expense objects for another reason the inject method is, of course, fine.
You're looking for the Enumerable#inject
method:
expenses_this_month.inject(0.0) {|total, expense| total + expense }
This method (borrowed from Smalltalk) takes the value passed to it (0.0 in this case) and sets an internal variable to that. It then calls the block with the value of that variable (as total
) and each successive element (as expense
), and sets the variable to whatever the block returns (in this case the sum of the total and the current element).
You may want to offload this calculation to the database, though, as kejadlen suggests, by using the #sum
method.
expenses_this_month.map(&:cost).sum
(shorter, although it creates an array in memory unlike reduce)
expenses_this_month.reduce(BigDecimal.new('0')) { |total, expense| total + expense.cost }
you need to remember to pass an initial value to reduce (otherwise it will return nil for empty array) and to use BigDecimal instead of regular floats when dealing with money.
Once you have returned the data, use the inject
method:
total = expenses_this_month.inject { |total, expense| total + expense.cost }
However, you should just rewrite your query:
total = expenses.sum(:cost, :conditions => ['date >= ? and date <= ?',
Date.today.beginning_of_month, Date.today.end_of_month])
If you're using Rails, you can use the built-in sum
class method (assuming Expense
is the class name).
expenses_this_month = Expense.sum('cost',
:conditions => ['date >= ? and date <= ?',
Date.today.beginning_of_month,
Date.today.end_of_month])
精彩评论