Method: Enumerable#inject

Defined in:
enum.c

#inject(symbol) ⇒ Object #inject(initial_value, symbol) ⇒ Object #inject {|memo, value| ... } ⇒ Object #inject(initial_value) {|memo, value| ... } ⇒ Object

Returns the result of applying a reducer to an initial value and the first element of the Enumerable. It then takes the result and applies the function to it and the second element of the collection, and so on. The return value is the result returned by the final call to the function.

You can think of

[ a, b, c, d ].inject(i) { |r, v| fn(r, v) }

as being

fn(fn(fn(fn(i, a), b), c), d)

In a way the inject function injects the function between the elements of the enumerable.

inject is aliased as reduce. You use it when you want to reduce a collection to a single value.

The Calling Sequences

Let’s start with the most verbose:

enum.inject(initial_value) do |result, next_value|
  # do something with +result+ and +next_value+
  # the value returned by the block becomes the
  # value passed in to the next iteration
  # as +result+
end

For example:

product = [ 2, 3, 4 ].inject(1) do |result, next_value|
  result * next_value
end
product #=> 24

When this runs, the block is first called with 1 (the initial value) and 2 (the first element of the array). The block returns 1*2, so on the next iteration the block is called with 2 (the previous result) and 3. The block returns 6, and is called one last time with 6 and 4. The result of the block, 24 becomes the value returned by inject. This code returns the product of the elements in the enumerable.

First Shortcut: Default Initial value

In the case of the previous example, the initial value, 1, wasn’t really necessary: the calculation of the product of a list of numbers is self-contained.

In these circumstances, you can omit the initial_value parameter. inject will then initially call the block with the first element of the collection as the result parameter and the second element as the next_value.

[ 2, 3, 4 ].inject do |result, next_value|
  result * next_value
end

This shortcut is convenient, but can only be used when the block produces a result which can be passed back to it as a first parameter.

Here’s an example where that’s not the case: it returns a hash where the keys are words and the values are the number of occurrences of that word in the enumerable.

freqs = File.read("README.md")
  .scan(/\w{2,}/)
  .reduce(Hash.new(0)) do |counts, word|
    counts[word] += 1
    counts
  end
freqs #=> {"Actions"=>4,
           "Status"=>5,
           "MinGW"=>3,
           "https"=>27,
           "github"=>10,
           "com"=>15, ...

Note that the last line of the block is just the word counts. This ensures the return value of the block is the result that’s being calculated.

Second Shortcut: a Reducer function

A reducer function is a function that takes a partial result and the next value, returning the next partial result. The block that is given to inject is a reducer.

You can also write a reducer as a function and pass the name of that function (as a symbol) to inject. However, for this to work, the function

  1. Must be defined on the type of the result value

  2. Must accept a single parameter, the next value in the collection, and

  3. Must return an updated result which will also implement the function.

Here’s an example that adds elements to a string. The two calls invoke the functions String#concat and String#+ on the result so far, passing it the next value.

s = [ "cat", " ", "dog" ].inject("", :concat)
s #=> "cat dog"
s = [ "cat", " ", "dog" ].inject("The result is:", :+)
s #=> "The result is: cat dog"

Here’s a more complex example when the result object maintains state of a different type to the enumerable elements.

class Turtle

  def initialize
    @x = @y = 0
  end

  def move(dir)
    case dir
    when "n" then @y += 1
    when "s" then @y -= 1
    when "e" then @x += 1
    when "w" then @x -= 1
    end
    self
  end
end

position = "nnneesw".chars.reduce(Turtle.new, :move)
position  #=>> #<Turtle:0x00000001052f4698 @y=2, @x=1>

Third Shortcut: Reducer With no Initial Value

If your reducer returns a value that it can accept as a parameter, then you don’t have to pass in an initial value. Here :* is the name of the times function:

product = [ 2, 3, 4 ].inject(:*)
product # => 24

String concatenation again:

s = [ "cat", " ", "dog" ].inject(:+)
s #=> "cat dog"

And an example that converts a hash to an array of two-element subarrays.

nested = {foo: 0, bar: 1}.inject([], :push)
nested # => [[:foo, 0], [:bar, 1]]

Overloads:

  • #inject(symbol) ⇒ Object

    Returns:

  • #inject(initial_value, symbol) ⇒ Object

    Returns:

  • #inject {|memo, value| ... } ⇒ Object

    Yields:

    • (memo, value)

    Returns:

  • #inject(initial_value) {|memo, value| ... } ⇒ Object

    Yields:

    • (memo, value)

    Returns:



1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
# File 'enum.c', line 1040

static VALUE
enum_inject(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE init, op;
    rb_block_call_func *iter = inject_i;
    ID id;
    int num_args;

    if (rb_block_given_p()) {
        num_args = rb_scan_args(argc, argv, "02", &init, &op);
    }
    else {
        num_args = rb_scan_args(argc, argv, "11", &init, &op);
    }

    switch (num_args) {
      case 0:
        init = Qundef;
        break;
      case 1:
        if (rb_block_given_p()) {
            break;
        }
        id = rb_check_id(&init);
        op = id ? ID2SYM(id) : init;
        init = Qundef;
        iter = inject_op_i;
        break;
      case 2:
        if (rb_block_given_p()) {
            rb_warning("given block not used");
        }
        id = rb_check_id(&op);
        if (id) op = ID2SYM(id);
        iter = inject_op_i;
        break;
    }

    if (iter == inject_op_i &&
        SYMBOL_P(op) &&
        RB_TYPE_P(obj, T_ARRAY) &&
        rb_method_basic_definition_p(CLASS_OF(obj), id_each)) {
        return ary_inject_op(obj, init, op);
    }

    memo = MEMO_NEW(init, Qnil, op);
    rb_block_call(obj, id_each, 0, 0, iter, (VALUE)memo);
    if (UNDEF_P(memo->v1)) return Qnil;
    return memo->v1;
}