Enumerating Enumerable: Enumerable#each_with_index

by Joey deVilla on August 1, 2008

Here’s number 11 in the Enumerating Enumerable series, in which I’m trying to outdo RubyDoc.org in their documentation of Ruby’s key Enumerable module. In this article, I cover the each_with_index method.

In case you missed the earlier articles, they’re listed and linked below:

Enumerable#each_with_index and Arrays

When used with on an array and given a block, each_with_index iterates through the array, passing each item in the array and the number representing its order in the array to the block. Once again, I’ll use the “Justice League” example:

Note that unlike the each_cons and each_slice, each_with_indexdoesn’t return nil, but the original array, just like each does. each_cons and each_slice are newer methods that were introduced in Ruby 1.9; perhaps Matz and company decided that it made more sense for “each” methods to return nil from now on. each and each_with_index have return the original array as they always have, since making changes to these methods might break a lot of existing Ruby code. Hooray for those little inconsistencies that creep into every programming language!

Enumerable#each_with_index and Hashes

When used with on a hash and given a block, each_with_index iterates through the hash, converting each item in the hash into a two-element array (where the first element is the key and the second element is the corresponding value), and then passing that array and the number representing its order in the original array to the block. Once again, I’ll use the “Enterprise Crew” example:

Enumerable#each_with_index Oddities

Blocks with a Single Parameter

What happens if you use each_with_index with a block that has only one parameter? You’d think that the block would accept the item and index passed to it as a two-element array, with the item as the first element and the corresponding index as the second element, but that’s not the case:

When each_with_index passes an item and its index to a block that takes only one parameter, the item goes into the paramter, and the index is discarded. Simply put, in this case, each_with_index behaves just like each.

Without a Block

What happens if you use each_with_index without a block to create an Enumerator object that spits out the next item when its next method is called? This:

Swift

1

2

3

4

5

6

7

8

9

10

11

hero=justice_league.each_with_index

=>#&lt;Enumerable::Enumerator:0x159cb98&gt;

hero.next

=>"Aquaman"

hero.next

=>"Batman"

hero.next

=>"Black Canary"

Note that calling next only yields the next item in the collection; the index information is lost. In this case, each_with_index behaves just like each.