0

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]
the Tin Man
  • 158,662
  • 42
  • 215
  • 303

2 Answers2

2

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]
Andrey Deineko
  • 51,333
  • 10
  • 112
  • 145
2

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.

Community
  • 1
  • 1
Justin Hellreich
  • 575
  • 5
  • 15
  • "You can't get the average with map because it returns an array." The OP wants an array. Note : I didn't downvote. – Eric Duminil Feb 13 '17 at 21:40
  • Yup, I noticed that after. I figure I'll leave this up since it's a good explanation of both why his code doesn't work and how `map` and `reduce` work (I may have jumped right to the commented link on the question and assumed what the question was... my mistake). – Justin Hellreich Feb 13 '17 at 21:41
  • Also : Ruby comments are with `#`, not `//` – Eric Duminil Feb 13 '17 at 21:42
  • 1
    This by no means answers the OP. It should be either corrected to ***answer*** the ***question*** or deleted. – Andrey Deineko Feb 13 '17 at 22:58
  • 1
    Well actually it explains why OP's code doesn't work and gives some details on the methods used in the accepted answer. Also, the consensus [here](http://meta.stackexchange.com/questions/144452/is-it-okay-to-put-partial-answers) is that partial answers are better than no answer. If you link to a somewhere that argues that "It should be either corrected to answer the question or deleted" is the policy than I'll be happy to remove it. – Justin Hellreich Feb 13 '17 at 23:02