Deeper explanation of reduce / inject method in ruby - ruby

I've been racking my brains around this for some time.
When using reduce - why is the first element returned without executing the operation defined in the block? Or am I missing a crucial point in how reduce works?
In the following example:
arr = [1, 3, 5]
arr.reduce {|sum, n| sum + (n * 3) }
#=> 25
I would have expected the result to be 27.
Since:
0 + (1 * 3) = 3
3 + (3 * 3) = 12
12 + (5 * 3) = 27
After some time playing around with it I figured out that in the first "tick" - the object from the array just get's added to the sum instead of being multiplied. So that the calculation is more like:
??? = 1
1 + (3 * 3) = 10
10 + (5 * 3) = 25
Could someone help me figure out where I've gotten off the path?

It's in the docs.
If you do not explicitly specify an initial value for memo, then the first element of collection is used as the initial value of memo.

I had a similar issue with the default values in Ruby inject/reduce methods, so I've tried to visualize it:

I thnk that the explanation is in the help o¡f the methos in this case:
[1] pry(main)> cd Array
[2] pry(Array):1> ? reduce
From: enum.c (C Method):
Owner: Enumerable
Visibility: public
Signature: reduce(*arg1)
Number of lines: 33
Combines all elements of enum by applying a binary
operation, specified by a block or a symbol that names a
method or operator.
The inject and reduce methods are aliases. There
is no performance benefit to either.
If you specify a block, then for each element in enum
the block is passed an accumulator value (memo) and the element.
If you specify a symbol instead, then each element in the collection
will be passed to the named method of memo.
In either case, the result becomes the new value for memo.
At the end of the iteration, the final value of memo is the
return value for the method.
If you do not explicitly specify an initial value for memo,
then the first element of collection is used as the initial value
of memo.
# Sum some numbers
(5..10).reduce(:+) #=> 45
# Same using a block and inject
(5..10).inject { |sum, n| sum + n } #=> 45
# Multiply some numbers
(5..10).reduce(1, :*) #=> 151200
# Same using a block
(5..10).inject(1) { |product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo, word|
memo.length > word.length ? memo : word
end
longest #=> "sheep"
So you need to specify the first memo as 0, in your case is one:
[4] pry(Array):1> [1,3,5].reduce(0) {|sum, n| sum + (n * 3) }
=> 27

Related

I don't understand this method

I'm a beginner in Ruby and I don't understand what this code is doing, could you explain it to me, please?
def a(n)
s = 0
for i in 0..n-1
s += i
end
s
end
def defines a method. Methods can be used to run the same code on different values. For example, lets say you wanted to get the square of a number:
def square(n)
n * n
end
Now I can do that with different values and I don't have to repeat n * n:
square(1) # => 1
square(2) # => 4
square(3) # => 9
= is an assignment.
s = 0 basically says, behind the name s, there is now a zero.
0..n-1 - constructs a range that holds all numbers between 0 and n - 1. For example:
puts (0..3).to_a
# 0
# 1
# 2
# 3
for assigns i each consecutive value of the range. It loops through all values. So first i is 0, then 1, then ... n - 1.
s += i is a shorthand for s = s + i. In other words, increments the existing value of s by i on each iteration.
The s at the end just says that the method (remember the thing we opened with def) will give you back the value of s. In other words - the sum we accumulated so far.
There is your programming lesson in 5 minutes.
This example isn't idiomatic Ruby code even if it is syntactically valid. Ruby hardly ever uses the for construct, iterators are more flexible. This might seem strange if you come from another language background where for is the backbone of many programs.
In any case, the program breaks down to this:
# Define a method called a which takes an argument n
def a(n)
# Assign 0 to the local variable s
s = 0
# For each value i in the range 0 through n minus one...
for i in 0..n-1
# ...add that value to s.
s += i
end
# The result of this method is s, the sum of those values.
s
end
The more Ruby way of expressing this is to use times:
def a(n)
s = 0
# Repeat this block n times, and in each iteration i will represent
# a value in the range 0 to n-1 in order.
n.times do |i|
s += i
end
s
end
That's just addressing the for issue. Already the code is more readable, mind you, where it's n.times do something. The do ... end block represents a chunk of code that's used for each iteration. Ruby blocks might be a little bewildering at first but understanding them is absolutely essential to being effective in Ruby.
Taking this one step further:
def a(n)
# For each element i in the range 0 to n-1...
(0..n-1).reduce |sum, i|
# ...add i to the sum and use that as the sum in the next round.
sum + i
end
end
The reduce method is one of the simple tools in Ruby that's quite potent if used effectively. It allows you to quickly spin through lists of things and compact them down to a single value, hence the name. It's also known as inject which is just an alias for the same thing.
You can also use short-hand for this:
def a(n)
# For each element in the range 0 to n-1, combine them with +
# and return that as the result of this method.
(0..n-1).reduce(&:+)
end
Where here &:+ is shorthand for { |a,b| a + b }, just as &:x would be short for { |a,b| a.x(b) }.
As you are a beginner in Ruby, let's start from the small slices.
0..n-1 => [0, n-1]. E.g. 0..3 => 0, 1, 2, 3 => [0, 3]
for i in 0.. n-1 => this is a for loop. i traverses [0, n-1].
s += i is same as s = s + i
So. Method a(n) initializes s = 0 then in the for loop i traverse [0, n - 1] and s = s + i
At the end of this method there is an s. Ruby omits key words return. so you can see it as return s
def a(n)
s = 0
for i in 0..n-1
s += i
end
s
end
is same as
def a(n)
s = 0
for i in 0..n-1
s = s + i
end
return s
end
a(4) = 0 + 1 + 2 + 3 = 6
Hope this is helpful.
The method a(n) calculates the sums of the first n natural numbers.
Example:
when n=4, then s = 0+1+2+3 = 6
Let's go symbol by symbol!
def a(n)
This is the start of a function definition, and you're defining the function a that takes a single parameter, n - all typical software stuff. Notably, you can define a function on other things, too:
foo = "foo"
def foo.bar
"bar"
end
foo.bar() # "bar"
"foo".bar # NoMethodError
Next line:
s = 0
In this line, you're both declaring the variable s, and setting it's initial value to 0. Also typical programming stuff.
Notably, the value of the entire expression; s = 0, is the value of s after the assignment:
s = 0
r = t = s += 1 # You can think: r = (t = (s += 1) )
# r and t are now 1
Next line:
for i in 0..n-1
This is starting a loop; specifically a for ... in ... loop. This one a little harder to unpack, but the entire statement is basically: "for each integer between 0 and n-1, assign that number to i and then do something". In fact, in Ruby, another way to write this line is:
(0..n-1).each do |i|
This line and your line are exactly the same.
For single line loops, you can use { and } instead of do and end:
(0..n-1).each{|i| s += i }
This line and your for loop are exactly the same.
(0..n-1) is a range. Ranges are super fun! You can use a lot of things to make up a range, particularly, time:
(Time.now..Time.new(2017, 1, 1)) # Now, until Jan 1st in 2017
You can also change the "step size", so that instead of every integer, it's, say, every 1/10:
(0..5).step(0.1).to_a # [0.0, 0.1, 0.2, ...]
Also, you can make the range exclude the last value:
(0..5).to_a # [0, 1, 2, 3, 4, 5]
(0...5).to_a # [0, 1, 2, 3, 4]
Next line!
s += i
Usually read aloud a "plus-equals". It's literally the same as: s = s + 1. AFAIK, almost every operator in Ruby can be paired up this way:
s = 5
s -= 2 # 3
s *= 4 # 12
s /= 2 # 6
s %= 4 # 2
# etc
Final lines (we'll take these as a group):
end
s
end
The "blocks" (groups of code) that are started by def and for need to be ended, that's what you're doing here.
But also!
Everything in Ruby has a value. Every expression has a value (including assignment, as you saw with line 2), and every block of code. The default value of a block is the value of the last expression in that block.
For your function, the last expression is simply s, and so the value of the expression is the value of s, after all is said and done. This is literally the same as:
return s
end
For the loop, it's weirder - it ends up being the evaluated range.
This example may make it clearer:
n = 5
s = 0
x = for i in (0..n-1)
s += i
end
# x is (0..4)
To recap, another way to write you function is:
def a(n)
s = 0
(0..n-1).each{ |i| s = s + i }
return s
end
Questions?

Ruby #inject behavior is different from documentation

Taking a look at Ruby documentation on Enumerable class I noticed something interesting and I'd like to know why it happens this way.
At #inject description I've found these examples:
# Sum some numbers
(5..10).reduce(:+) #=> 45
# Same using a block and inject
(5..10).inject { |sum, n| sum + n } #=> 45
# Multiply some numbers
(5..10).reduce(1, :*) #=> 151200
# Same using a block
(5..10).inject(1) { |product, n| product * n } #=> 151200
Notice that when #inject is used for multiplication, it receives an initial value of 1. I thought this was necessary because otherwise product would receive 0 as start value (as it happens in the sum) and the multiplication would also be 0. In fact, if I run
p (1..5).inject(0) { |prod, n| prod * n }
I got
0
But then I run
p (1..5).inject { |sum, n| sum + n }
p (1..5).inject { |prod, n| prod * n }
and got
15
120
My questions are:
a) Why the documentation includes this 1 as initial value when, in fact, it is not needed?
and
b) What is the behavior of #inject when it comes to initializing the object being injected?
To answer your first question:
a) Why the documentation includes this 1 as initial value when, in fact, it is not needed?
inject does not take 1 as initial value, from the apidock:
If you do not explicitly specify an initial value for memo, then uses the first element of collection is used as the initial value of memo.
Answer of second question lies in the answer of first itself, as it initialises the object as the first element of array on which inject is being applied.

Why do I get a 'typeerror' when using inject in Ruby?

I am using this inject method to make a running total of values into an array. I am trying to figure out why I am getting an error.
def running_totals(myarray)
results = []
myarray.inject([]) do |sum,n|
results << sum + n
end
results
end
p running_totals([1,2,3,4,5])
I am getting the error
in `+': no implicit conversion of Fixnum into Array (TypeError)
When breaking this method down, isn't this the same as adding two integers and adding that into an array? I'm a bit confused here. Thanks for the help.
In the first iteration sum will be an array (as you specified an array as the default when calling inject([])) and you try to add a number to it. in the results << sum + n statement
Instead, set the initial value to 0, then add, then add the result to the array, then make sure you let sum get passed into the next iteration of inject.
def running_totals(myarray)
results = []
myarray.inject(0) do |sum,n| # First iteration sum will be 0.
sum += n # Add value to sum.
results << sum # Push to the result array.
sum # Make sure sum is passed to next iteration.
end
results
end
p running_totals([1,2,3,4,5]) #=> [1, 3, 6, 10, 15]
The result of results << sum + n is an array results and it's this that's replacing the sum value and so the next iteration you're trying to add a fixnum n into an array sum ... plus it doesn't help that you're initializing the value of sum to be an array.
Make sure that the last executed statement in your inject block is what you want the accumulated value to be.
def running_totals(myarray)
results = []
results << myarray.inject do |sum, n|
results << sum
sum + n
end
results
end
p running_totals([1,2,3,4,5])
=> [1, 3, 6, 10, 15]
Note that I moved the result of the inject into results array as well, so that the final value is also included, otherwise you'd only have the four values and would be missing the final (15) value.
The return value of the inject block is passed as the first argument the next time the block is called, so those have to match. In your code, you're passing an array as an intital value, and then returning an array; so far, so good. But inside the code block you treat that array parameter (sum) as a number, which won't work. Try this:
def running_totals(myarray)
myarray.inject([]) do |results,n|
results << n + (results.last || 0)
end
end
The [] passed as an argument to inject becomes the first value of results; the first array element (1 in your example) becomes the first value of n. Since results is empty, results.last is nil and the result of (results.last || 0) is 0, which we add to n to get 1, which we push onto results and then return that newly-modified array value from the block.
The second time into the block, results is the array we just returned from the first pass, [1], and n is 2. This time results.last is 1 instead of nil, so we add 1 to 2 to get 3 and push that onto the array, returning [1,3].
The third time into the block, results is [1,3], and n is 3, so it returns [1,3,6]. And so on.
According to ri, you have to return result of the computation from inject's block.
From: enum.c (C Method):
Owner: Enumerable
Visibility: public
Signature: inject(*arg1)
Number of lines: 31
Combines all elements of enum by applying a binary
operation, specified by a block or a symbol that names a
method or operator.
If you specify a block, then for each element in enum
the block is passed an accumulator value (memo) and the element.
If you specify a symbol instead, then each element in the collection
will be passed to the named method of memo.
In either case, the result becomes the new value for memo.
At the end of the iteration, the final value of memo is the
return value for the method.
If you do not explicitly specify an initial value for memo,
then uses the first element of collection is used as the initial value
of memo.
Examples:
# Sum some numbers
(5..10).reduce(:+) #=> 45
# Same using a block and inject
(5..10).inject {|sum, n| sum + n } #=> 45
# Multiply some numbers
(5..10).reduce(1, :*) #=> 151200
# Same using a block
(5..10).inject(1) {|product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo,word|
memo.length > word.length ? memo : word
end
longest
So your sample would work if you return computation result for each iteration, something like this:
def running_totals(myarray)
results = []
myarray.inject do |sum,n|
results << sum + n
results.last # return computation result back to Array's inject
end
results
end
Hope it helps.

Why ruby's inject doesn't sum correctly?

I'm not getting correct results from the following monkey-patching method in Integer:
def harm
1 + (2..self).inject{|sum, x| sum + 1/x.to_r}
end
2.harm #=> 3
it should return 3/2 instead, where is my mistake?
There are two problems here:
When you iterate across a closed range, such as 2..2, nothing actually happens:
(0..0).inject(){|s, x| s+= 99 }
# => 0
Which is why you get 3, as 1 + 2 is 3.
If you do not pass an argument into inject, it uses the first value you pass into the iterator as the starting memo, i.e. 2:
(2..2).inject(){|s, x| s+= 99 }
#=> 2
Putting in a 0 gets you an actual iteration:
(2..2).inject(0){|s, x| s+= 99 }
#=> 99
So try this instead in your method:
1 + (2..self).inject(0){|sum, x| sum + 1/x.to_r}
Standalone:
1 + (2..2).inject(0){|sum, x| sum + 1/x.to_r}
#=> 3/2
Here is the tip(you need to pass the initial value to the inject method):
def harm
1 + (2..2).inject(0){|sum, x| sum + 1/x.to_r}
end
harm # => (3/2)
Documentation of Enumerable#inject:
If you specify a block, then for each element in enum the block is passed an accumulator value (memo) and the element. If you specify a symbol instead, then each element in the collection will be passed to the named method of memo. In either case, the result becomes the new value for memo. At the end of the iteration, the final value of memo is the return value for the method.
If you do not explicitly specify an initial value for memo, then the first element of collection is used as the initial value of memo.
In the 1 minute of time that I decided to spend over your question, I was unable to realize what's wrong with your code. But I was able to write this method that does something similar to what you want to do:
class Integer
def harm
return 0 if self == 0
return -(-self).harm if self < 0
( 1 .. self ).map { |n| Rational 1, n }.reduce :+
end
end
0.harm #=> 0
2.harm #=> 3/2
7.harm #=> 363/140
-2.harm #=> (-3/2)
Note, though, that for large number, this highly readable code becomes inefficient, as it prepares the array in the memory before performing the summation.

Need a simple explanation of the inject method

[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
I'm looking at this code but my brain is not registering how the number 10 can become the result. Would someone mind explaining what's happening here?
You can think of the first block argument as an accumulator: the result of each run of the block is stored in the accumulator and then passed to the next execution of the block. In the case of the code shown above, you are defaulting the accumulator, result, to 0. Each run of the block adds the given number to the current total and then stores the result back into the accumulator. The next block call has this new value, adds to it, stores it again, and repeats.
At the end of the process, inject returns the accumulator, which in this case is the sum of all the values in the array, or 10.
Here's another simple example to create a hash from an array of objects, keyed by their string representation:
[1,"a",Object.new,:hi].inject({}) do |hash, item|
hash[item.to_s] = item
hash
end
In this case, we are defaulting our accumulator to an empty hash, then populating it each time the block executes. Notice we must return the hash as the last line of the block, because the result of the block will be stored back in the accumulator.
inject takes a value to start with (the 0 in your example), and a block, and it runs that block once for each element of the list.
On the first iteration, it passes in the value you provided as the starting value, and the first element of the list, and it saves the value that your block returned (in this case result + element).
It then runs the block again, passing in the result from the first iteration as the first argument, and the second element from the list as the second argument, again saving the result.
It continues this way until it has consumed all elements of the list.
The easiest way to explain this may be to show how each step works, for your example; this is an imaginary set of steps showing how this result could be evaluated:
[1, 2, 3, 4].inject(0) { |result, element| result + element }
[2, 3, 4].inject(0 + 1) { |result, element| result + element }
[3, 4].inject((0 + 1) + 2) { |result, element| result + element }
[4].inject(((0 + 1) + 2) + 3) { |result, element| result + element }
[].inject((((0 + 1) + 2) + 3) + 4) { |result, element| result + element }
(((0 + 1) + 2) + 3) + 4
10
The syntax for the inject method is as follows:
inject (value_initial) { |result_memo, object| block }
Let's solve the above example i.e.
[1, 2, 3, 4].inject(0) { |result, element| result + element }
which gives the 10 as the output.
So, before starting let's see what are the values stored in each variables:
result = 0 The zero came from inject(value) which is 0
element = 1 It is first element of the array.
Okey!!! So, let's start understanding the above example
Step :1 [1, 2, 3, 4].inject(0) { |0, 1| 0 + 1 }
Step :2 [1, 2, 3, 4].inject(0) { |1, 2| 1 + 2 }
Step :3 [1, 2, 3, 4].inject(0) { |3, 3| 3 + 3 }
Step :4 [1, 2, 3, 4].inject(0) { |6, 4| 6 + 4 }
Step :5 [1, 2, 3, 4].inject(0) { |10, Now no elements left in the array, so it'll return 10 from this step| }
Here Bold-Italic values are elements fetch from array and the simply Bold values are the resultant values.
I hope that you understand the working of the #inject method of the #ruby.
The code iterates over the four elements within the array and adds the previous result to the current element:
1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
What they said, but note also that you do not always need to provide a "starting value":
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
is the same as
[1, 2, 3, 4].inject { |result, element| result + element } # => 10
Try it, I'll wait.
When no argument is passed to inject, the first two elements are passed into the first iteration. In the example above, result is 1 and element is 2 the first time around, so one less call is made to the block.
The number you put inside your () of inject represents a starting place, it could be 0 or 1000.
Inside the pipes you have two place holders |x, y|. x = what ever number you had inside the .inject('x'), and the secound represents each iteration of your object.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
1 + 5 = 6
2 + 6 = 8
3 + 8 = 11
11 + 4 = 15
Inject applies the block
result + element
to each item in the array. For the next item ("element"), the value returned from the block is "result". The way you've called it (with a parameter), "result" starts with the value of that parameter. So the effect is adding the elements up.
tldr; inject differs from map in one important way: inject returns the value of the last execution of the block whereas map returns the array it iterated over.
More than that the value of each block execution passed into the next execution via the first parameter (result in this case) and you can initialize that value (the (0) part).
Your above example could be written using map like this:
result = 0 # initialize result
[1, 2, 3, 4].map { |element| result += element }
# result => 10
Same effect but inject is more concise here.
You'll often find an assignment happens in the map block, whereas an evaluation happens in the inject block.
Which method you choose depends on the scope you want for result. When to not use it would be something like this:
result = [1, 2, 3, 4].inject(0) { |x, element| x + element }
You might be like all, "Lookie me, I just combined that all into one line," but you also temporarily allocated memory for x as a scratch variable that wasn't necessary since you already had result to work with.
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
is equivalent to the following:
def my_function(r, e)
r+e
end
a = [1, 2, 3, 4]
result = 0
a.each do |value|
result = my_function(result, value)
end
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
In plain English, you are going through (iterating) through this array ([1,2,3,4]). You will iterate through this array 4 times, because there are 4 elements (1, 2, 3, and 4). The inject method has 1 argument (the number 0), and you will add that argument to the 1st element (0 + 1. This equals 1). 1 is saved in the "result". Then you add that result (which is 1) to the next element (1 + 2. This is 3). This will now be saved as the result. Keep going: 3 + 3 equals 6. And finally, 6 + 4 equals 10.
This is a simple and fairly easy to understand explanation:
Forget about the "initial value" as it is somewhat confusing at the beginning.
> [1,2,3,4].inject{|a,b| a+b}
=> 10
You can understand the above as: I am injecting an "adding machine" in between 1,2,3,4. Meaning, it is 1 ♫ 2 ♫ 3 ♫ 4 and ♫ is an adding machine, so it is the same as 1 + 2 + 3 + 4, and it is 10.
You can actually inject a + in between them:
> [1,2,3,4].inject(:+)
=> 10
and it is like, inject a + in between 1,2,3,4, making it 1 + 2 + 3 + 4 and it is 10. The :+ is Ruby's way of specifying + in the form of a symbol.
This is quite easy to understand and intuitive. And if you want to analyze how it works step by step, it is like: taking 1 and 2, and now add them, and when you have a result, store it first (which is 3), and now, next is the stored value 3 and the array element 3 going through the a + b process, which is 6, and now store this value, and now 6 and 4 go through the a + b process, and is 10. You are essentially doing
((1 + 2) + 3) + 4
and is 10. The "initial value" 0 is just a "base" to begin with. In many cases, you don't need it. Imagine if you need 1 * 2 * 3 * 4 and it is
[1,2,3,4].inject(:*)
=> 24
and it is done. You don't need an "initial value" of 1 to multiply the whole thing with 1. This time, it is doing
(((1 * 2) * 3) * 4)
and you get the same result as
1 * 2 * 3 * 4
This code doesn't allow the possibility of not passing a starting value, but may help explain what's going on.
def incomplete_inject(enumerable, result)
enumerable.each do |item|
result = yield(result, item)
end
result
end
incomplete_inject([1,2,3,4], 0) {|result, item| result + item} # => 10
One day, I was also banging my head against the default values in Ruby inject/reduce methods, so I've tried to visualize my issue:
Start here and then review all methods that take blocks.
http://ruby-doc.org/core-2.3.3/Enumerable.html#method-i-inject
Is it the block that confuses you or why you have a value in the method?
Good question though. What is the operator method there?
result.+
What does it start out as?
#inject(0)
Can we do this?
[1, 2, 3, 4].inject(0) { |result, element| result.+ element }
Does this work?
[1, 2, 3, 4].inject() { |result = 0, element| result.+ element }
You see I'm building on to the idea that it simply sums all the elements of the array and yields a number in the memo you see in the docs.
You can always do this
[1, 2, 3, 4].each { |element| p element }
to see the enumerable of the array get iterated through. That's the basic idea.
It's just that inject or reduce give you a memo or an accumulator that gets sent out.
We could try to get a result
[1, 2, 3, 4].each { |result = 0, element| result + element }
but nothing comes back so this just acts the same as before
[1, 2, 3, 4].each { |result = 0, element| p result + element }
in the element inspector block.
There is another form of .inject() method That is very helpful
[4,5].inject(&:+) That will add up all the element of the area
A common scenario that arises when using a collection of any sort, is to get perform a single type of operation with all the elements and collect the result.
For example, a sum(array) function might wish to add all the elements passed as the array and return the result.
A generalized abstraction of same functionality is provided in Ruby in the name of reduce (inject is an alias). That is, these methods iterate over a collection and accumulate the value of an operation on elements in a base value using an operator and return that base value in the end.
Let's take an example for better understanding.
>>> (5..10).inject(1) {|product, n| product * n }
=> 151200
In above example, we have the following elements: a base value 1, an enumerable (5..10), and a block with expressions instructing how to add the calculated value to base value (i.e., multiply the array element to product, where product is initialized with base value)
So the execution follows something like this:
# loop 1
n = 1
product = 1
return value = 1*1
# loop 2
n = 2
product = 1
return value = 1*2
n = 3
product = 2
return value = 2*3
..
As you can notice, the base value is continually updated as the expression loops through the element of container, thus returning the final value of base value as result.
Is the same as this:
[1,2,3,4].inject(:+)
=> 10

Resources