
Once again, it’s Enumerating Enumerable, my series of articles in which I attempt to outdo Ruby-Doc.org’s documentation of Ruby’s Enumerable module. In this article, I cover the find_index method, which was introduced in Ruby 1.9.
In case you missed any of the previous articles, they’re listed and linked below:
- all?
- any?
- collect / map
- count
- cycle
- detect / find
- drop
- drop_while
- each_cons
- each_slice
- each_with_index
- entries / to_a
- find_all / select
Enumerable#find_index Quick Summary

| In the simplest possible terms | What’s the index of the first item in the collection that meets the given criteria? | 
| Ruby version | 1.9 | 
| Expects | A block containing the criteria. | 
| Returns | 
The index of the item in the collection that matches the criteria, if there is one.nil, if no item in the collection matches the crtieria. | 
| RubyDoc.org’s entry | Enumerable#find_index | 
Enumerable#find_index and Arrays
When used on an array, find_index passes each item in the array to the given block and either:
- Stops when the current item causes the block to return a value that evaluates to true(that is, anything that isn’tfalseornil) and returns the index of that item, or
- Returns nilif there is no item in the array that causes the block to return a value that evaluates totrue.
Some examples:
# How about an array of the name of the first cosmonauts and astronauts,
# listed in the chronological order of the missions?
mission_leaders = ["Gagarin", "Shepard", "Grissom", "Titov", "Glenn", "Carpenter",
"Nikolayev", "Popovich"]
=> ["Gagarin", "Shepard", "Grissom", "Titov", "Glenn", "Carpenter", "Nikolayev",
"Popovich"]
# Yuri Gagarin was the first in space
mission_leaders.find_index{|leader| leader == "Gagarin"}
=> 0
# John Glenn was the fifth
mission_leaders.find_index{|leader| leader == "Glenn"}
=> 4
# And James Tiberius Kirk is not listed in the array
kirk_present = mission_leaders.find_index{|leader| leader == "Kirk"}
=> nil
Enumerable#find_index and Hashes
When used on a hash, find_index passes each key/value pair in the hash to the block, which you can “catch” as either:
- A two-element array, with the key as element 0 and its corresponding value as element 1, or
- Two separate items, with the key as the first item and its corresponding value as the second item.
As with arrays, find_index:
- Stops when the current item causes the block to return a value that evaluates to true(that is, anything that isn’tfalseornil) and returns the index of that item, or
- Returns nilif there is no item in the array that causes the block to return a value that evaluates totrue.
Some examples:
require 'date'
=> true
# These are the names of the first manned spaceships and their launch dates
launch_dates = {"Kedr"              => Date.new(1961, 4, 12),
                "Freedom 7"         => Date.new(1961, 5, 5),
                "Liberty Bell 7"    => Date.new(1961, 7, 21),
                "Orel"              => Date.new(1961, 8, 6),
                "Friendship 7"      => Date.new(1962, 2, 20),
                "Aurora 7"          => Date.new(1962, 5, 24),
                "Sokol"             => Date.new(1962, 8, 11),
                "Berkut"            => Date.new(1962, 8, 12)}
=> {"Kedr"=>#<Date: 4874803/2,0,2299161>, "Freedom 7"=>#<Date: 4874849/2,0,2299161>,
"Liberty Bell 7"=>#<Date: 4875003/2,0,2299161>, "Orel"=>#<Date: 4875035/2,0,2299161>,
"Friendship 7"=>#<Date: 4875431/2,0,2299161>, "Aurora 7"=>#<Date: 4875617/2,0,2299161>,
"Sokol"=>#<Date: 4875775/2,0,2299161>, "Berkut"=>#<Date: 4875777/2,0,2299161>}
# Where in the list is John Glenn's ship, the Friendship 7?
launch_dates.find_index{|ship, date| ship == "Friendship 7"}
=> 4
# Where in the list is the first mission launched in August 1962?
launch_dates.find_index{|ship, date| date.year == 1962 && date.month == 8}
=> 6
# The same thing, expressed a little differently
launch_dates.find_index{|launch| launch[1].year == 1962 && launch[1].month == 8}
=> 6
Using find_index as a Membership Test
Although Enumerable has a method for checking whether an item is a member of a collection (the include? method and its synonym, member?), find_index is a more powerful membership test for two reasons:
- include?/- member?only check membership by using the- ==operator, while- find_indexlets you define a block to set up all sorts of tests.- include?/- member?asks “Is there an object X in the collection equal to my object Y?” while- find_indexcan be used to ask “Is there an object X in the collection that matches these criteria?”
- include?/- member?returns- trueif there is an object X in the collection that is equal to the given object Y.- find_indexgoes one step further: not only can it be used to report the equivalent of- trueif there is an object X in the collection that is equal to the given object Y, it also reports its location in the collection.
A quick example of this use in action:
# Once again, the mission leaders
mission_leaders = ["Gagarin", "Shepard", "Grissom", "Titov", "Glenn", "Carpenter",
"Nikolayev", "Popovich"]
=> ["Gagarin", "Shepard", "Grissom", "Titov", "Glenn", "Carpenter", "Nikolayev",
 "Popovich"]
# Yuri Gagarin is in the list
gagarin_in_list = mission_leaders.find_index {|leader| leader == "Gagarin"}
=> 0
# Captain James T. Kirk is not
kirk_in_list = mission_leaders.find_index {|leader| leader == "Kirk"}
=> nil
# gagarin_in_list is 0, which as a non-false and non-nil value evaluates as true.
# We can use it as both a membership test *and* as his location in the list.
p "Gagarin's there. He's number #{gagarin_in_list + 1} in the list." if gagarin_in_list
"Gagarin's there. He's number 1 in the list."
=> "Gagarin's there. He's number 1 in the list."
# kirk_in_list is nil, which is one of Ruby's two "false" values.
# Let's use it with the "something OR something else" idiom that
# many Ruby programmers like.
kirk_in_list || (p "You only *think* he wasn't there.")
"You only *think* he wasn't there."
=> "You only *think* he wasn't there."
Parts that Haven’t Been Implemented Yet
Ruby-Doc.org’s documentation is generated from the comments in the C implementation of Ruby. It mentions a way of calling find_index that is just like calling include?/member?:
# What the docs say (does not work yet!)
["Alice", "Bob", "Carol"].find_index("Bob")
=> 1
# What happens with the current version of Ruby 1.9
["Alice", "Bob", "Carol"].find_index("Bob")
ArgumentError: wrong number of arguments(1 for 0)
...
Ruby 1.9 is considered to be a work in progress, so I suppose it’ll get implemented in a later release.