Come Correct with Inject and Collect

Adarsh Pandit

Here’s a refactoring example for a simple Ruby math problem using the inject method.

The goal is to generate an n by n multiplication matrix. Pretty straightforward. Let’s make a first pass:

def multiplication_table(n)
  results = []

  (1..n).each do |row_index|
    row = []
    (1..n).each { |column_index| row << row_index * column_index }
    results << row
  end

  results.each do |row|
    header = '%-3s ' * row.length
    puts header % row
  end
end

multiplication_table(7)
1   2   3   4   5   6   7
2   4   6   8   10  12  14
3   6   9   12  15  18  21
4   8   12  16  20  24  28
5   10  15  20  25  30  35
6   12  18  24  30  36  42
7   14  21  28  35  42  49

It does the job, but it’s nothing to look at. We can condense using in-line blocks and fancy curly braces, but first let’s try using inject.

Quick background: Inject iterates over a set using a block with a collector, which is a variable whose value is passed back each round, making it good for iterative uses. The block’s return value is passed into the collector (make sure you return something or you’ll get a nil). Also, the return value of the whole inject block is the final collector value.

Simple example from the docs:

(5..10).inject {|sum, n| sum + n }            #=> 45

The inject method takes a parameter, which initializes the collector value (default is to set to the first value). You can pass in an empty array and add values:

def multiplication_table(n)
  results = (1..n).map do |row_index|
    (1..n).inject([]) { |row, n| row + [row_index * n] }
  end

  results.each { |row| puts '%-3s ' * row.length % row }
end

So that’s inject with the iterative collect (Whoo-aaaa - got you all in check)

Note: For a more in-depth discussion of inject, read Mike Burns’ recent post.