rubyguides

Array#index

arr.index(object) { |element| ... } → Integer | nil

Array#index returns the position of the first element that matches, scanning in forward order from index 0. It works either by value (using ==) or by a block predicate. The method is an alias for Array#find_index — pick one name per codebase and stick with it, since the two are interchangeable.

The three call forms

Form 1: by value

Pass the value you’re looking for as the argument:

[:foo, 'bar', 2, 'bar'].index('bar')   # => 1
[:foo, 'bar', 2, 'bar'].index(:foo)    # => 0
[:foo, 'bar', 2, 'bar'].index('nope')  # => nil

Returns the first index where element == object. nil means “no match”; the method does not raise. If the value appears more than once, you get the leftmost position.

Form 2: by block

Pass a block that returns truthy on a match. The block receives only the element, not the index:

nums = [10, 20, 30, 40, 50]
nums.index { |n| n > 25 }              # => 2

users = [{name: 'Ada'}, {name: 'Bo'}, {name: 'Cy'}]
users.index { |u| u[:name] == 'Bo' }   # => 1

The search short-circuits on the first truthy return. Anything other than false or nil counts as a match, so puts, arithmetic, and string concatenation all qualify as predicates (though I’d usually write a real boolean expression for clarity).

Form 3: no arg, no block

Returns an Enumerator you can chain to a block later:

e = [10, 20, 30].index
# => #<Enumerator: [10, 20, 30]:index>
e.each { |n| n == 20 }   # => 1

This is handy when you want to compose index with other Enumerable methods, or hand the enumerator off to a caller.

Argument plus block is a warning

Since Ruby 2.6, passing both an argument and a block prints a warning and ignores the block:

[:foo, 'bar', 2].index('bar') { |e| e == 2 }
# => 1
# warning: given block not used

Pick one form per call. If you wrote the block, delete the argument; if the argument is what you wanted, drop the block.

nil inside the array

nil is a real element when it appears in the array. index(nil) returns the position of the first nil, not the literal nil “not found” result:

[1, nil, 3, nil].index(nil)           # => 1
[1, nil, 3, nil].index { |x| x.nil? } # => 1

Because the result is nil in both the “found a nil” and “not found” cases, disambiguate with include? first if the array may contain nils:

arr.index(x) if arr.include?(x)

Common Mistakes

  1. Expecting -1 like JavaScript’s indexOf. Ruby returns nil for a miss, so an unless guard or a fetch with a custom block both work. The fetch form raises a specific error so the call site tells you what was missing:
    arr.fetch(arr.index(x)) { raise "not found" }
  2. Mutating the array inside the block. index does not copy. Doing arr.delete_at(arr.index { ... }) shifts indices and can skip elements on the next pass. Compute the index first, then mutate.
  3. Confusing it with String#index. Different method — String#index takes a substring or Regexp, not arbitrary elements. The name overlaps; the contract does not.
  4. Confusing it with each_index. each_index iterates indices; index returns one.
  5. Using == when you wanted eql?. 2 == 2.0 is true (so index(2.0) finds the integer 2), but "foo" == :foo is false. If you need hash-key semantics, write the predicate yourself: arr.index { |e| e.eql?(target) }.

See Also

  • Array#find — same “first match” idea, but returns the element instead of the position.
  • Array#include? — boolean membership test; a cheap precheck before calling index.
  • Array#each_index — iterate indices, not values.