Ruan Odendaal
Ruan Odendaal

Reputation: 83

How to calculate averages of items in array

I am writing a method that takes an array and returns another array of the averages of each number and the next number in the array.

I tried using map but have been staring at it too long and can't see where I'm going wrong:

array = [ 1, 3, 5, 1, -10]
array.map! { |a| (a + arr[a+1])/2 }

I expect:

[ 2, 4, 3, -4.5]

Upvotes: 0

Views: 110

Answers (2)

Justin Hellreich
Justin Hellreich

Reputation: 575

Edit: the key takeaway to follow is that you are indexing your array with the an array value, not an index.

There's lots of available one line answers to your question so I'll try to address your code:

array.map! { |a| (a + arr[a + 1]) / 2 }

Map

The key thing to understand with map is that it acts on an array and returns an array (you used map! which is in place, but we can disregard that for now. You can't get the average with map because it returns an array.

Ex:

[1, 2, 3, 4].map { |num| num * num } # returns [1, 4, 9, 16]

So let's walk through your code and see what it is doing:

[1, 2, 3].map { |a| (a + arr[a + 1]) / 2 }

The first call to |a| (a + arr[a + 1]) / 2, a will be 1. So the first element of your returned array is 1 + arr[1 + 1] / 2 = 1 + 3 / 2 ~= 2 And so on. Clearly this isn't what you want.

Inject/Reduce

Now for the one line solutions (I took this from here):

arr.inject { |sum, el| sum + el }.to_f / arr.size

inject is the same as reduce, which is foldr in some languages. This is a bit more complicated than map. Basically the method |sum, el| sum + el is applied to the first two elements, and the result of that is applied to the third element and so on until the end of the array.

So if our array is [1, 2, 3, 4], a arithmetic call stack might look something like this:

((1 + 2) + 3) + 4

With these two methods, you should be able to accomplish what you need, i.e. an array of averages. Or at the very least this should help you understand the other solutions.

Upvotes: 2

Andrey Deineko
Andrey Deineko

Reputation: 52347

You can use Enumerable#each_cons:

array = [ 1, 3, 5, 1, -10]
array.each_cons(2).map { |a| a.inject(:+) / 2.0 }
#=> [2.0, 4.0, 3.0, -4.5]

Summarizing it into a method:

def each_cost_avg(array, size = 2)
  array.each_cons(size).map { |a| a.inject(:+) / size.to_f }
end

each_cost_avg(array, 2) #=> [2.0, 4.0, 3.0, -4.5]
each_cost_avg(array, 3) #=> [3.0, 3.0, -1.3333333333333333]
each_cost_avg(array, 4) #=> [2.5, -0.25]

Upvotes: 2

Related Questions