Computer Science Canada

The power of inject

Author:  wtd [ Tue Apr 05, 2005 2:37 am ]
Post subject:  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:

code:
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.

code:
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.

code:
arr = [1, 3, 23, 56, 789]

product = arr.inject(0) { |a, b| a * b }


Questions? Comments?

Author:  jamonathin [ Tue Apr 05, 2005 7:45 am ]
Post subject: 

What exactly does this do/mean Confused
code:
{ |a, b| a * b }

Author:  Tony [ Tue Apr 05, 2005 9:18 am ]
Post subject: 

it's a block where |a,b| represent two values in an array and a*b is the function applied to the said values.

Author:  jamonathin [ Tue Apr 05, 2005 11:43 am ]
Post subject: 

Does the ' a * b ' have to be in the | a,b |, it can't be like
code:
{ |a, b| c * d }

Author:  Tony [ Tue Apr 05, 2005 12:31 pm ]
Post subject: 

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
Ruby:

{|a,b| 42}

So to make it clear - you're not restricted to array's objects. You can, for example, have a *5

Author:  wtd [ Tue Apr 05, 2005 12:33 pm ]
Post subject: 

tony wrote:
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.

code:
|a, b|


Says "the block accepts two arguments, and we'll call them a and b."

code:
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:

code:
[1, 3, 23, 56, 789]


It would look like:

code:
((((0 + 1) + 3) + 23) + 56) + 789
(((1 + 3) + 23) + 56) + 789
((4 + 23) + 56) + 789
(27 + 56) + 789
83 + 789
872

Author:  jamonathin [ Wed Apr 06, 2005 9:59 am ]
Post subject: 

ohhh ok, I get it guys, thanks lots Very Happy

Author:  wtd [ Wed Apr 06, 2005 1:01 pm ]
Post subject: 

tony wrote:
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
Ruby:

{|a,b| 42}


If you're not using a block's parameter, then don't include it at all.

code:
$ 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>

Author:  wtd [ Fri Nov 11, 2005 5:56 pm ]
Post subject: 

What can we do with inject?

We can reverse an Array.

code:
>> 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.

code:
>> foo = [3, 5, 1, 8]
=> [3, 5, 1, 8]
>> foo.inject(0) { |a, _| a + 1 }
=> 4


We can get the keys from a Hash.

code:
>> bar = {"hello" => "world", "foo" => "bar"}
=> {"foo"=>"bar", "hello"=>"world"}
>> bar.inject([]) { |a, b| a << b.first }
=> ["foo", "hello"]


We can invert a Hash.

code:
>> 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.


: