inject
inject(p1 = v1, p2 = v2)
public
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 #=> "sheep"
Parameters for Hash#inject
When running inject on a Hash, the hash is first converted to an array before being passed through.
The typical Enumerable#inject approach would be to simply capture the value:
array.inject(...) do |c, v| end
In the case of a Hash, v is actually a key/value pair Array. That is the key is v.first and the value is v.last, however using the pair this way is awkward and can lead to confusion.
Better to simply expand the parameters in the block definition:
hash.inject(...) do |c, (k, v)| end
Where c is the traditional carry variable and k/v represent key and value respectively.
Re: Convert an Array of Arrays to a Hash using inject
If you’re sure you have a two-level array (no other arrays inside the pairs) and exactly two items in each pair, then it’s faster and shorter to use this:
array = [['A', 'a'], ['B', 'b'], ['C', 'c']] hash = Hash[*array.flatten]
For more than two-level deep arrays this will give the wrong result or even an error (for some inputs).
array = [['A', 'a'], ['B', 'b'], ['C', ['a', 'b', 'c']]] hash = Hash[*array.flatten] # => {"A"=>"a", "B"=>"b", "C"=>"a", "b"=>"c"}
But if you’re running Ruby 1.8.7 or greater you can pass an argument to Array#flatten and have it flatten only one level deep:
# on Ruby 1.8.7+ hash = Hash[*array.flatten(1)] # => {"A"=>"a", "B"=>"b", "C"=>["a", "b", "c"]}
Convert an Array of Arrays to a Hash using inject
Converting an array of arrays to a hash using inject:
array = [['A', 'a'], ['B', 'b'], ['C', 'c']] hash = array.inject({}) do |memo, values| memo[values.first] = values.last memo end hash # => {'A' => 'a', 'B' => 'b', 'C' => 'c'}
Array expansion in blocks
The syntax can be improved as changing the second parameter of the block (values) and using an array of two variables instead, which will be used by Ruby as the key and value of “array”.
array = [['A', 'a'], ['B', 'b'], ['C', 'c']] hash = array.inject({}) do |memo, (key, value)| memo[key] = value memo end hash # => {'A' => 'a', 'B' => 'b', 'C' => 'c'}
From the official docs
enum.inject(initial) {| memo, obj | block } => obj enum.inject {| memo, obj | block } => obj
Combines the elements of enum by applying the block to an accumulator value (memo) and each element in turn. At each step, memo is set to the value returned by the block. The first form lets you supply an initial value for memo. The second form uses the first element of the collection as a the initial value (and skips that element while iterating).
# Sum some numbers (5..10).inject {|sum, n| sum + n } #=> 45 # Multiply some numbers (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" # find the length of the longest word longest = %w{ cat sheep bear }.inject(0) do |memo,word| memo >= word.length ? memo : word.length end longest #=> 5
Calculating on an enumerable
Inject can easily be used to sum an enumerable or to get the product of it
[100, 200, 1000].inject(0) { |sum, value| sum += value } # => 1300 [100, 200, 1000].inject(1) { |sum, value| sum *= value } # => 20000000 # You can access members and move down in the data structures, too points.inject(0) { |sum, point| sum += point.y }
In the case of the first two examples, an easier way to do it in Ruby 1.9 is to use reduce:
[100, 200, 1000].reduce :+ # => 1300 [100, 200, 1000].reduce :* # => 20000000
Look at reduce for more examples on how to use this.
Highlight keywords in a text
Case-insensitive
keywords.inject(text) { |text, keyword| text.gsub(/(#{keyword})/i, "<strong>\\1</strong>") }
<strong> can be replace by whatever HTML tag you want for hightlighting (<b>, <i>, …)
RE: Convert an Array of Arrays to a Hash using inject
Another way to convert an array of arrays to a hash using inject:
array = [['A', 'a'], ['B', 'b'], ['C', 'c']] hash = array.inject({}) do |memo, values| memo.merge!(values.first => values.last) end hash # => {'A' => 'a', 'B' => 'b', 'C' => 'c'}