Iterators

The Enumerator Class

The concept of iterator is implemented, in Ruby, by the Enumerator class, most iterator methods return an object of this class.

An Enumerator has the method to_a to produce an array, the method next to extract (and delete) an element, a method peek to fetch the current value (without deletion), a method rewind to begin again the sequence and a method each that loops over the elements giving them, in turn, to a block:

a=Enumerator.new([1,2,3])

a.each {|k| print k}        => 123

a.next  => 1
a.next  => 2    # extract, and  consume the element
a.peek  => 3    # only fetch the current element
a.peek  => 3    # always the same element without a "*next*"
a.next  => 3
a.next  => raise a StopIteration exception

a.rewind        # to begin again the sequence
a.next  => 1

The Enumerable Module

Most iterators are implemented in the Enumerable module; this module is mixed into classes which contain sequences: Array, Hashes, Range, Struct, IO and Dir; since Ruby 1.9 the class String doesn't use the Enumerable module but implements all its own iterators.

Some member of the module Enumerable are listed in the following table:

all? true if all the elements are true. Es.: [0,true,nil].all? => false
any? true if some elements is true. Es.: [0,true,nil].any? => true
collect ; map array obtained by the block: (1..2).collect {|a| a*2} => [2, 4]
count counts true values from block: (1..6).count {|a| a<3 } => 2
cycle(n) cycles n time over the sequence: [1,2].cycle(2) {|a| print a} => 1212
detect return the first true element: (1..6).detect {|a| a>3 }  => 4
drop(n) array without the first n elements: [1,2,3,4].drop(2) => [3, 4]
drop_wile drops until block returns true: [1,2,3,4].drop_while {|a| a < 3 } => [3, 4]
take_while take until block returns false: [1,2,3,4].take_while {|a| a < 3 } => [1, 2]
each_slice(n) loops on groups of n values:(1..4).each_slice(2){|a| print a}=>[1,2][3,4]
each_with_index value and index to the loop:(1..3).each_with_index{|a,i| print a,i}=>102132
find_all ; select all elements for which the block is true: (1..3).find_all {|a| a>1}=>[2, 3]
reject all elements for which the block is false: (1..3).reject {|a| a>1} => [1]
find_index index of first element giving a true block: (1..3).find_index{|a| a>1} => 1
first(n);take(n) first n elements: (1..4).first(2) => [1, 2]
grep(pattern) array of matching elements:["a","b","c"].grep(/b|c/){|i| i}=>["b","c"]
include? true if an element is included: (1..3).include? => true
max maximum value: (1..3).max => 3
min minimum value: `` (1..3).min => 1 ``
max_by value giving the greater block: (1..3).max_by {|a| 1.0/a }  => 1
min_by value giving the smaller block: (1..3).min_by {|a| 1.0/a }  => 3
minmax minimum and maximum value (1..3).minmax => [1, 3]
one? true if the block return true only once: (1..3).one? {|a| a==2 } => true
none? true if the block is always false (1..3).none? {|a| a==2 }=> false
reverse_each loops in reverse order: (1..3).reverse_each {|a| print a } => 321
sort sort elements: [3,2,1].sort => [1, 2, 3]
sort_by sorting based on the block: (1..3).sort_by {|a| 1.0/a } => [3, 2, 1]

flat_map can be used to build an array, by appending consecutive results of the block:

(1..2).flat_map{|a| [a,a+10,a+20]} => [1, 11, 21, 2, 12, 22]

group_by can be used to build an hash, the block gives the keys:

(1..3).group_by { |a| a+100 } => {101=>[1], 102=>[2], 103=>[3]}

minmax can have an associated block, giving a comparison expression which utilized the "<=>" operator;

also sort can have an associated block:

(1..3).minmax {|a,b| 1.0/a <=> 1.0/b } => [3, 1]

(1..3).sort {|a,b| 1.0/a <=> 1.0/b }   => [3, 2, 1]

Some iterator for numerics

upto 3.upto(5)  {|i| print i, " " } => 3 4 5 (last value is included )
downto 3.downto(1) {|n| print n, ".. " }  => 3.. 2.. 1..
times 3.times  {|n| print n, ".. " }  =>  0.. 1.. 2..
step 1.step(6, 2) {|i| print i, " " }   => 1 3 5

If the block is missing these methods return an Enumerator object which can be converted to an array with the to_a method

3.upto(5).to_a     => [3, 4, 5]

3.downto(1).to_a   => [3, 2, 1]

3.times.to_a       => [0, 1, 2]

1.step(6, 2).to_a  => [1, 3, 5]

12.2.step(14.0,0.5).to_a  => [12.2, 12.7, 13.2, 13.7]

Some Iterators for Strings

each_char loops on characters: "abcd".each_char {|k| print k+"z" }azbzczdz=> "abcd"
each_byte;bytes loops on bytes: "abcd".each_byte {|k| print k,"-" } => 97-98-99-100-
eachy_line loops on lines (keep the final "n" characters)
upto loops on a sequence of strings: "a".upto("d"){|k| print k} => abcd

Also these methods, if the block is not present, return an iterator which can be converted to an array.

Some Iterators for Arrays

each gives each item to the block: [1,2,3].each {|k| print k} => 123
reverse_each gives items in reverse order: [1,2,3].each {|k| print k} => 123
each_index gives indexes to the block: [1,2,3].each_index {|k| print k} => 012
map array with block results: [1,2,3].map {|k| k+1}  => [2,3,4]
collect;collect! array of block results: [1,2,3].collect {|i| i*i } => [1, 4, 9]
delete_if if block false deletes elements: [1,2,3].delete_if {|x| x.even?} => [1, 3]
keep_if keeps if block true: [1,2,3].keep_if { |x| x.even?  } => [2]
select;select! elements with a true block: [1,2,3].select { |x| x.even? } => [2]
rindex index of element of first true block: [1,2,3].rindex {|k| k==2}=> 1
uniq ; uniq! elements with unique block value: [1,2,3,4].uniq {|k| k.even?}=> [1,2]

The creator of arrays can also be used as an iterator:

a=Array.new(3) { |i| i}   => [0, 1, 2]

Some Iterators for Hashes

Hashes has iterators similar to the iterators for arrays, but give different arguments to the block:

each ; each_pair gives each pair key,value {1=>"a",2=>"b"}.each {|k,v| print k,v} => 1a2b
each_key gives the keys to the block: {1=>"a",2=>"b"}.each_key {|k| print k} =>12
each_value gives the values to the block: {1=>"a",2=>"b"}.each_value{|k| print k} =>ab
delete_if if block false deletes: {1=>"a",2=>"b"}.delete_if {|k,v| k==1 => {2=>"b"}
keep_if keeps if block true: {1=>"a",2=>"b"}.keep_if {|k,v| k==1 } => {1=>"a"}
select,select! hash of elements with true block: {1=>"a",2=>"b"}.select {|k,v| k==1}{1=>"a"}

The merge method can be used associated with a block: when there are duplicated keys the block is executed and the new value for the key is the the block result

{1=>"a",2=>"b"}.merge({1=>"x",3=>"z"}) {|key,v1,v2|  v1+v2 } => {1=>"ax", 2=>"b", 3=>"z"}