
-----------------------------------
wtd
Tue Apr 05, 2005 2:37 am

The power of inject
-----------------------------------
For operations on arrays Ruby offers us a powerful feature from the functional programming realm.  What's classically known as "fold" or "reduce" there is the inject method in Ruby (the name is from Smalltalk).

Let's consider the simple ask of summing an array of integers.

The simple imperative solution we've probably all seen is:

arr = [1, 3, 23, 56, 789]
sum = 0

arr.each { |x| sum += x }

This isn't too bad.  Ruby makes the loop quite expressive.  

But we can do better.

Inject involves taking each element of an array and adding it to the result of doing the same operation on the previous element.  Of course, since there is no previous result for he first elemen, we supply an initial value.  For addition, let's start with an initial value of zero.

arr = [1, 3, 23, 56, 789]

sum = arr.inject(0) { |a, b| a + b }

Consider also an example for finding the product of an array of numbers.

arr = [1, 3, 23, 56, 789]

product = arr.inject(0) { |a, b| a * b }

Questions?  Comments?

-----------------------------------
jamonathin
Tue Apr 05, 2005 7:45 am


-----------------------------------
What exactly does this do/mean  :?  { |a, b| a * b }

-----------------------------------
Tony
Tue Apr 05, 2005 9:18 am


-----------------------------------
it's a block where |a,b| represent two values in an array and a*b is the function applied to the said values.

-----------------------------------
jamonathin
Tue Apr 05, 2005 11:43 am


-----------------------------------
Does the ' a * b ' have to be in the | a,b |, it can't be like { |a, b| c * d }

-----------------------------------
Tony
Tue Apr 05, 2005 12:31 pm


-----------------------------------
well since nether C nor D relate to the block of variables, you might as well pre-calculate the result of the function, such as

{|a,b| 42}

So to make it clear - you're not restricted to array's objects. You can, for example, have a *5

-----------------------------------
wtd
Tue Apr 05, 2005 12:33 pm


-----------------------------------
it's a block where |a,b| represent two values in an array and a*b is the function applied to the said values.

Yes, it's a block.  

|a, b|

Says "the block accepts two arguments, and we'll call them a and b."

a + b

Is saying the block returns "the sum of a and b."

In this case, though, a and b do not represent two values taken from the array.  Rather, a represents the value of the previous operation and b the current element in the array.

For an array like:

[1, 3, 23, 56, 789]

It would look like:

((((0 + 1) + 3) + 23) + 56) + 789
(((1 + 3) + 23) + 56) + 789
((4 + 23) + 56) + 789
(27 + 56) + 789
83 + 789
872

-----------------------------------
jamonathin
Wed Apr 06, 2005 9:59 am


-----------------------------------
ohhh ok, I get it guys, thanks lots :D

-----------------------------------
wtd
Wed Apr 06, 2005 1:01 pm


-----------------------------------
well since nether C nor D relate to the block of variables, you might as well pre-calculate the result of the function, such as

{|a,b| 42}


If you're not using a block's parameter, then don't include it at all.

 $ irb
irb(main):001:0> [1,2,3].inject(0) { |a, b| 42 }
=> 42
irb(main):002:0> [1,2,3].inject(0) { 42 }
=> 42
irb(main):003:0>

-----------------------------------
wtd
Fri Nov 11, 2005 5:56 pm


-----------------------------------
What can we do with inject?

We can reverse an Array.

>> foo = [3, 5, 1, 8]
=> [3, 5, 1, 8]
>> foo.inject([]) { |a, b| a.unshift b }
=> [8, 1, 5, 3]

We can find the length of an Array.

>> foo = [3, 5, 1, 8]
=> [3, 5, 1, 8]
>> foo.inject(0) { |a, _| a + 1 }
=> 4

We can get the keys from a Hash.

>> bar = {"hello" => "world", "foo" => "bar"}
=> {"foo"=>"bar", "hello"=>"world"}
>> bar.inject([]) { |a, b| a  ["foo", "hello"]

We can invert a Hash.

>> bar = {"hello" => "world", "foo" => "bar"}
=> {"foo"=>"bar", "hello"=>"world"}
>> bar.inject({}) { |a, b| a[b.last] = b.first; a }
=> {"world"=>"hello", "bar"=>"foo"}

These are just a few examples.
