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
- Expecting
-1like JavaScript’sindexOf. Ruby returnsnilfor a miss, so anunlessguard or afetchwith a custom block both work. Thefetchform raises a specific error so the call site tells you what was missing:arr.fetch(arr.index(x)) { raise "not found" } - Mutating the array inside the block.
indexdoes not copy. Doingarr.delete_at(arr.index { ... })shifts indices and can skip elements on the next pass. Compute the index first, then mutate. - Confusing it with
String#index. Different method —String#indextakes a substring or Regexp, not arbitrary elements. The name overlaps; the contract does not. - Confusing it with
each_index.each_indexiterates indices;indexreturns one. - Using
==when you wantedeql?.2 == 2.0istrue(soindex(2.0)finds the integer2), but"foo" == :fooisfalse. 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 callingindex.Array#each_index— iterate indices, not values.