views:

247

answers:

3

Assuming the @houses array is set up as follows:

house1.price = 10
house2.price = 20
house3.price = 30
@houses << house1
@houses << house2
@houses << house3

This is the starting point of our calculation and we want to find the average price of a house:

total = 0
average = 0
for h in @houses
 total += h.price
end
average = total/@houses.size

This seems like quite a lot of typing just to get an average.

Is there a better way?

+13  A: 

Use the inject method on an enumerable collection. Inject lets you pass in an initial value for an 'accumulator' (0 in this case), then apply some operation to each element in the list, and return a new value for the accumulator to be passed into the next iteration.

The final value of the accumulator is then returned from the inject call.

So in this case, we just add up all the house prices into the accumulator, then finally divide by the total number.

You can get funkier and probably compress it down more with some Ruby skillz, but this is reasonably understandable, and only iterates through the list once to add up the values.

@houses.inject(0){|total, house| total + house.price} / @houses.size
madlep
+9  A: 

Madlep's answer will work in any version of Ruby from the past several years. But if you're using Ruby 1.8.7 or later, you can express it a little bit more concisely as @houses.collect(&:price).inject(:+)/@houses.size.

Incidentally, I had assumed this technique would be slower since it has to loop twice, but thanks to optimizations in Ruby itself, it's actually much faster up to a couple million items in my tests on Ruby 1.8.7, and faster (but not by as much) even past 10 million items in Ruby 1.9. Goes to show you the importance of profiling.

Chuck
M. Pelosi
Chuck
Didn't know they'd added that in 1.9. Awesome :D
madlep
Ah. I get it. I found some examples in the ruby doc section on inject + reduce. Thanks for explaining. I'd love to use this but sadly I can't upgrade my project to 1.87.
M. Pelosi
I think madlep's answer is perfectly good. But if you ever do want to use the ` def to_proc() @proc_version ||= proc {|r, *a| r.send self, *a} end end`. It won't be as fast as 1.9's version, but it's functionally the same.
Chuck
A: 

If you're in a Rails app, you can do array.sum/array.size