[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 1
[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
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
.
The code iterates over the four elements within the array and adds the previous result to the current element:
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
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.
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.