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:

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’t false or nil ) and returns the index of that item, or

(that is, anything that isn’t or ) and returns the index of that item, or Returns nil if there is no item in the array that causes the block to return a value that evaluates to true .

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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # 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’t false or nil ) and returns the index of that item, or

(that is, anything that isn’t or ) and returns the index of that item, or Returns nil if there is no item in the array that causes the block to return a value that evaluates to true .

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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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" = > # & lt ; Date : 4874803 / 2 , 0 , 2299161 & gt ; , "Freedom 7" =& gt ; # & lt ; Date : 4874849 / 2 , 0 , 2299161 & gt ; , "Liberty Bell 7" =& gt ; # & lt ; Date : 4875003 / 2 , 0 , 2299161 & gt ; , "Orel" =& gt ; # & lt ; Date : 4875035 / 2 , 0 , 2299161 & gt ; , "Friendship 7" =& gt ; # & lt ; Date : 4875431 / 2 , 0 , 2299161 & gt ; , "Aurora 7" =& gt ; # & lt ; Date : 4875617 / 2 , 0 , 2299161 & gt ; , "Sokol" =& gt ; # & lt ; Date : 4875775 / 2 , 0 , 2299161 & gt ; , "Berkut" =& gt ; # & lt ; Date : 4875777 / 2 , 0 , 2299161 & gt ; } # 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_index lets 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_index can be used to ask “Is there an object X in the collection that matches these criteria?” include? / member? returns true if there is an object X in the collection that is equal to the given object Y. find_index goes one step further: not only can it be used to report the equivalent of true if 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." 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # 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) ... 1 2 3 4 5 6 7 8 # 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.