Collections

This section documents Ruby’s collection classes. A collection is any class that represents a collection of values. Array and Hash are the key collection classes in Ruby, and the standard library adds a Set class. Each of these collection classes mixes in the Enumerable module, which means that Enumerable methods are universal collection methods.

Enumerable Objects

The Enumerable module is a mixin that implements a number of useful methods on top of the each iterator. The Array, Hash, and Set classes described below all include Enumerable and therefore implement all of the methods described here. Range and IO are other noteworthy enumerable classes. Enumerable was covered briefly in Enumerable Objects. This section provides more detailed coverage.

Note that some enumerable classes have a natural enumeration order that their each method follows. Arrays, for example, enumerate their elements in order of increasing array index. Range enumerates in ascending order. And IO objects enumerate lines of text in the order in which they are read from the underlying file or socket. In Ruby 1.9, Hash and Set (which is based on Hash) enumerate their elements in the order in which they were inserted. Prior to Ruby 1.9, however, these classes enumerate their elements in what is essentially an arbitrary order.

Many Enumerable methods return a processed version of the enumerable collection or a selected subcollection of its elements. Usually, if an Enumerable method returns a collection (rather than a single value selected from a collection), the collection is an Array. This is not always the case, however. The Hash class overrides the reject method so that it returns a Hash object instead of an array, for example. Whatever the precise return value, it is certain that a collection returned by an Enumerable method will itself be enumerable.

Iterating and converting collections

By definition, any Enumerable object must have an each iterator. Enumerable provides a simple variant each_with_index, which yields an element of the collection and an integer. For arrays, the integer is the array index. For IO objects, the integer is the line number (starting at 0). For other objects, the integer is what the array index would be if the collection was converted to an array:

(5..7).each {|x| print x }                 # Prints "567"
(5..7).each_with_index {|x,i| print x,i }  # Prints "506172"

In Ruby 1.9, Enumerable defines cycle, which iterates repeatedly through the elements of the collection, looping forever until the block you provide explicitly terminates the iteration with break or return or by raising an exception. During its first pass through the Enumerable object, cycle saves the elements into an array and then subsequently iterates from the array. This means that after the first pass through the collection, modifications to that collection do not affect the behavior of cycle.

each_sliceand each_cons are iterators that yield subarrays of a collection. They are available in Ruby 1.8 with require 'enumerator' and are part of the core library in Ruby 1.9 (and 1.8.7). each_slice(n) iterates the enumerable values in “slices” of size n:

(1..10).each_slice(4) {|x| print x } # Prints "[1,2,3,4][5,6,7,8][9,10]"

each_cons is similar to each_slice, but it uses a “sliding window” on the enumerable collection:

(1..5).each_cons(3) {|x| print x }    # Prints "[1,2,3][2,3,4][3,4,5]"

The collect method applies a block to each element of a collection and collects the return values of the block into a new array. map is a synonym; it maps the elements of a collection to the elements of an array by applying a block to each element:

data = [1,2,3,4]                        # An enumerable collection
roots = data.collect {|x| Math.sqrt(x)} # Collect roots of our data
words = %w[hello world]                 # Another collection
upper = words.map {|x| x.upcase }       # Map to uppercase

The zip method interleaves the elements of one enumerable collection with the elements of zero or more other collections, and yields an array of elements (one from each collection) to the associated block. If no block is provided, the return value is an array of arrays:

(1..3).zip([4,5,6]) {|x| print x.inspect } # Prints "[1,4][2,5][3,6]"
(1..3).zip([4,5,6],[7,8]) {|x| print x}    # Prints "14725836"
(1..3).zip('a'..'c') {|x,y| print x,y }    # Prints "1a2b3c"
p (1..3).zip('a'..'z')                     # Prints [[1,"a"],[2,"b"],[3,"c"]]
p (1..3).zip('a'..'b')                     # Prints [[1,"a"],[2,"b"],[3,nil]]

Enumerable defines a to_a method (and a synonym entries) that converts any enumerable collection into an array. to_a is included in this section because the conversion obviously involves iterating the collection. The elements of the resulting array appear in whatever order the each iterator yields them:

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

If you require 'set', all Enumerable objects gain a to_set conversion method as well. Sets are described in detail in Sets:

require 'set'
(1..3).to_set     # => #<Set: {1, 2, 3}>

Enumerators and external iterators

Enumerators and their use as external iterators are fully documented in Enumerators and External Iterators. This section is just a brief recap, with examples, of the detailed descriptions in Chapter 5.

Enumerators are of class Enumerable::Enumerator, which has a surprisingly small number of methods for such a powerful iteration construct. Enumerators are primarily a feature of Ruby 1.9 (and 1.8.7) but some enumerator functionality is available in Ruby 1.8 by requiring the enumerator library. Create an Enumerator with to_enum or its alias enum_for, or simply by calling an iterator method without the block it expects:

e = [1..10].to_enum              # Uses Range.each
e = "test".enum_for(:each_byte)  # Uses String.each_byte
e = "test".each_byte             # Uses String.each_byte

Enumerator objects are Enumerable objects with an each method that is based on some other iterator method of some other object. In addition to being Enumerable proxy objects, an enumerator also behaves as an external iterator. To obtain the elements of a collection using an external iterator, simply call next repeatedly until it raises StopIteration. The Kernel.loop iterator rescues StopIteration for you. After next raises StopIteration, a subsequent call will typically begin a new iteration, assuming the underlying iterator method allows repeated iterations (iterators reading from a file don’t allow that, for example). If repeated iterations are possible, you can restart an external iterator before StopIteration has been raised by calling rewind:

"Ruby".each_char.max       # => "y"; Enumerable method of Enumerator
iter = "Ruby".each_char    # Create an Enumerator
loop { print iter.next }   # Prints "Ruby"; use it as external iterator
print iter.next            # Prints "R": iterator restarts automatically
iter.rewind                # Force it to restart now
print iter.next            # Prints "R" again

Given any enumerator e, you can obtain a new enumerator with e.with_index. As the name implies, this new enumerator yields an index (or iteration number) along with whatever value the original iterator would yield:

# Print "0:R
1:u
2:b
3:y
"
"Ruby".each_char.with_index.each {|c,i| puts "#{i}:#{c} }

Finally, note that enumerators, like all Enumerable objects, are splattable: you can prefix an enumerator with an asterisk to expand it into individual values for method invocation or parallel assignment.

Sorting collections

One of the most important methods of Enumerable is sort. It converts the enumerable collection to an array and sorts the elements of that array. By default, the sort is done according to the <=> method of the elements. If a block is provided, however, then it is passed pairs of elements and should return –1, 0, or +1 to indicate their relative order:

w = Set['apple','Beet','carrot']  # A set of words to sort
w.sort                         # ['Beet','apple','carrot']: alphabetical
w.sort {|a,b| b<=>a }          # ['carrot','apple','Beet']: reverse
w.sort {|a,b| a.casecmp(b) }   # ['apple','Beet','carrot']: ignore case
w.sort {|a,b| b.size<=>a.size} # ['carrot','apple','Beet']: reverse length

If the block you associate with sort must do substantial computation in order to perform its comparison, then it is more efficient to use sort_by instead. The block associated with sort_by will be called once for each element in the collection, and should return a numeric “sort key” for that element. The collection will then be sorted by ascending order of the sort key. This way, a sort key is only computed once for each element, rather than twice for each comparison:

# Case-insensitive sort
words = ['carrot', 'Beet', 'apple']
words.sort_by {|x| x.downcase}       # => ['apple', 'Beet', 'carrot']

Searching collections

Enumerable defines several methods for searching for single elements within a collection. include? and its synonym member? search for an element equal to (using ==) their argument:

primes = Set[2, 3, 5, 7]
primes.include? 2        # => true
primes.member? 1         # => false

The find method, and its synonym detect, apply the associated block to each element of the collection in turn. If the block returns anything other than false or nil, then find returns that element and stops iterating. If the block always returns nil or false, then find returns nil:

# Find the first subarray that includes the number 1
data = [[1,2], [0,1], [7,8]]
data.find {|x| x.include? 1}     # => [1,2]
data.detect {|x| x.include? 3}   # => nil: no such element

The find_index method (new in Ruby 1.9) is like the index method of Array: it returns the index of a specific element or of the first element that matches a block:

data.find_index [0,1]              # => 1: the second element matches
data.find_index {|x| x.include? 1} # => 0: the first element matches
data.find_index {|x| x.include? 3} # => nil: no such element

Note that the return value of find_index is not terribly useful for collections like hashes and sets that do not use numeric indexes.

Enumerable defines other searching methods that return a collection of matches rather than a single match. We cover these methods in the next section.

Selecting subcollections

The select method selects and returns elements of a collection for which a block returns a non-nil, non-false value. A synonym for this method is find_all; it works like the find method but returns an array of all matching elements:

(1..8).select {|x| x%2==0}    # => [2,4,6,8]: select even elements
(1..8).find_all {|x| x%2==1}  # => [1,3,5,7]: find all odd elements

reject is the opposite of select; the elements in the returned array are those for which the block returns false or nil.

primes = [2,3,5,7]
primes.reject {|x| x%2==0}  # => [3,5,7]: reject the even ones

If you want both to select and reject elements of a collection, use partition. It returns an array of two arrays. The first subarray holds elements for which the block is true, and the second subarray holds elements for which the block is false:

(1..8).partition {|x| x%2==0}  # => [[2, 4, 6, 8], [1, 3, 5, 7]]

The group_by method of Ruby 1.9 is a generalization of partition. Rather than treating the block as a predicate and returning two groups, group_by takes the return value of the block and uses it as a hash key. It maps that key to an array of all collection elements for which the block returned that value. For example:

# Group programming languages by their first letter
langs = %w[ java perl python ruby ]
groups = langs.group_by {|lang| lang[0] }
groups # => {"j"=>["java"], "p"=>["perl", "python"], "r"=>["ruby"]}

grep returns an array of elements that match the argument value, determining matching with the case equality operator (===) of the argument. When used with a regular expression argument, this method works like the Unix command-line utility grep. If a block is associated with the call, it is used to process matching elements, as if collect or map were called on the results of grep:

langs = %w[ java perl python ruby ]
langs.grep(/^p/)                    # => [perl, python]: start with 'p'
langs.grep(/^p/) {|x| x.capitalize} # => [Perl, Python]: fix caps
data = [1, 17, 3.0, 4]
ints = data.grep(Integer)           # => [1, 17, 4]: only integers
small = ints.grep(0..9)             # [1,4]: only in range

In Ruby 1.9, the selection methods described previously are augmented by first, take, drop, take_while, and drop_while. first returns the first element of an Enumerable object, or, given an integer argument n, an array containing the first n elements. take and drop expect an integer argument. take behaves just like first; it returns an array of the first n elements of the Enumerable receiver object. drop does the opposite; it returns an array of all elements of the Enumerable except for the first n:

p (1..5).first(2)      # => [1,2]
p (1..5).take(3)       # => [1,2,3]
p (1..5).drop(3)       # => [4,5]

take_while and drop_while expect a block instead of an integer argument. take_while passes elements of the Enumerable object to the block in turn, until the block returns false or nil for the first time. Then it returns an array of the previous elements for which the block returned true. drop also passes elements to the block in turn until the block returns false or nil for the first time. Then, however, it returns an array containing the element for which the block returned false and all subsequent elements:

[1,2,3,nil,4].take_while {|x| x }  # => [1,2,3]: take until nil
[nil, 1, 2].drop_while {|x| !x }   # => [1,2]: drop leading nils

The Array class defines its own efficient versions of these taking and dropping methods that do not require arrays to be iterated with each.

Reducing collections

Sometimes we want to reduce an enumerable collection to a single value that captures some property of the collection. min and max are methods that perform a reduction, returning the smallest or largest element of the collection (assuming that the elements are mutually comparable with <=>):

[10, 100, 1].min    # => 1
['a','c','b'].max   # => 'c'
[10, 'a', []].min   # => ArgumentError: elements not comparable

min and max can take a block like sort can, to compare two elements. In Ruby 1.9, it is easier to use min_by and max_by instead:

langs = %w[java perl python ruby]    # Which has the longest name?
langs.max {|a,b| a.size <=> b.size } # => "python": block compares 2
langs.max_by {|word| word.length }   # => "python": Ruby 1.9 only

Ruby 1.9 also defines minmax and minmax_by, which compute both the minimum and maximum value of a collection and return them as a two-element array [min,max]:

(1..100).minmax                   # => [1,100] min, max as numbers
(1..100).minmax_by {|n| n.to_s }  # => [1,99]  min, max as strings

any? andall? are predicates that also perform reductions. They apply a predicate block to elements of the collection. all? returns true if the predicate is true (that is, not nil and not false) for all elements of the collection. any? returns true if the predicate is true for any one of the elements. In Ruby 1.9, none? returns true only if the predicate never returns a true value. Also in 1.9, one? returns true only if the predicate returns a true value for one, and only one, element of the collection. Invoked without blocks, these methods simply test the elements of the collection themselves:

c = -2..2
c.all? {|x| x>0}    # => false: not all values are > 0
c.any? {|x| x>0}    # => true: some values are > 0
c.none? {|x| x>2}   # => true: no values are > 2
c.one? {|x| x>0}    # => false: more than one value is > 0
c.one? {|x| x>2}    # => false: no values are > 2
c.one? {|x| x==2}   # => true: one value == 2
[1, 2, 3].all?      # => true: no values are nil or false
[nil, false].any?   # => false: no true values
[].none?            # => true: no non-false, non-nil values    

Another Ruby 1.9 addition is the count method: it returns the number of elements in the collection that equal a specified value, or the number for which an associated block returns true:

a = [1,1,2,3,5,8]
a.count(1)                # => 2: two elements equal 1
a.count {|x| x % 2 == 1}  # => 4: four elements are odd

Finally, inject is a general purpose method for reducing a collection. Ruby 1.9 defines reduce as an alias for inject. The block associated with a call to inject expects two arguments. The first is an accumulated value; the second is an element from the collection. The accumulated value for the first iteration is the argument passed to inject. The block return value on one iteration becomes the accumulated value for the next iteration. The return value after the last iteration becomes the return value of inject. Here are some examples:

# How many negative numbers?
(-2..10).inject(0) {|num, x| x<0 ? num+1 : num }  # => 2

# Sum of word lengths
%w[pea queue are].inject(0) {|total, word| total + word.length }  # => 11

If no argument is passed to inject, then the first time the block is invoked, it is passed the first two elements of the collection. (Or, if there is only one element in the collection, inject simply returns that element.) This form of inject is useful for a number of common operations:

sum = (1..5).inject {|total,x| total + x}  # => 15
prod = (1..5).inject {|total,x| total * x} # => 120
max = [1,3,2].inject {|m,x| m>x ? m : x}   # => 3
[1].inject {|total,x| total + x}           # => 1: block never called

In Ruby 1.9, you can pass a symbol that names a method (or operator) to inject instead of specifying a block. Each element in the collection will be passed to the named method of the accumulated value, and its result will become the new accumulated value. It is common to use the reduce synonym when invoking the method with a symbol in this way:

sum = (1..5).reduce(:+)                    # => 15
prod = (1..5).reduce(:*)                   # => 120
letters = ('a'..'e').reduce("-", :concat)  # => "-abcde"

Arrays

Arrays are probably the most fundamental and commonly used data structure in Ruby programming. We covered array literals and indexing operators in Arrays. This section builds on that earlier one, demonstrating the rich API implemented by the Array class.

Creating arrays

Arrays can be created with array literals, or with the classmethod Array.new or the class operator Array.[]. Examples:

[1,2,3]             # Basic array literal
[]                  # An empty array
[]                  # Arrays are mutable: this empty array is different
%w[a b c]           # => ['a', 'b', 'c']: array of words
Array[1,2,3]        # => [1,2,3]: just like an array literal

# Creating arrays with the new() method
empty = Array.new             # []: returns a new empty array
nils = Array.new(3)           # [nil, nil, nil]: three nil elements
copy = Array.new(nils)        # Make a new copy of an existing array
zeros = Array.new(4, 0)       # [0, 0, 0, 0]: four 0 elements
count = Array.new(3){|i| i+1} # [1,2,3]: three elements computed by block

# Be careful with repeated objects
a=Array.new(3,'a')  # => ['a','a','a']: three references to the same string
a[0].upcase!        # Capitalize the first element of the array
a                   # => ['A','A','A']: they are all the same string!
a=Array.new(3){'b'} # => ['b','b','b']: three distinct string objects
a[0].upcase!;       # Capitalize the first one
a                   # => ['B','b','b']: the others are still lowercase

In addition to the Array factory methods, a number of other classes define to_a methods that return arrays. In particular, any Enumerable object, such as a Range or Hash, can be converted to an array with to_a. Also, array operators, such as +, and many array methods, such as slice, create and return new arrays rather than altering the receiving array in place.

Array size and elements

The following code shows how to determine the length of an array, and demonstrates a variety of ways to extract elements and subarrays from an array:

# Array length
[1,2,3].length     # => 3
[].size            # => 0: synonym for length
[].empty?          # => true
[nil].empty?       # => false
[1,2,nil].nitems   # => 2: number of non-nil elements (Ruby 1.8 only)
[1,2,nil].count(nil)    # => 1: # of nils (Enumerable method in Ruby 1.9)
[1,2,3].count {|x| x>2} # => 1: # of elts matching block (Ruby 1.9)

# Indexing single elements
a = %w[a b c d]    # => ['a', 'b', 'c', 'd']
a[0]               # => 'a': first element
a[-1]              # => 'd': last element
a[a.size-1]        # => 'd': last element
a[-a.size]         # => 'a': first element
a[5]               # => nil: no such element
a[-5]              # => nil: no such element
a.at(2)            # => 'c': just like [] for single integer argument
a.fetch(1)         # => 'b': also like [] and at
a.fetch(-1)        # => 'd': works with negative args
a.fetch(5)         # => IndexError!: does not allow out-of-bounds
a.fetch(-5)        # => IndexError!: does not allow out-of-bounds
a.fetch(5, 0)      # => 0: return 2nd arg when out-of-bounds
a.fetch(5){|x|x*x} # => 25: compute value when out-of-bounds
a.first            # => 'a': the first element
a.last             # => 'd': the last element
a.sample           # Ruby 1.9: return one element at random
a.sample(n)        # Ruby 1.9: return array of n random elements

# Indexing subarrays
a[0,2]             # => ['a','b']: two elements, starting at 0
a[0..2]            # => ['a','b','c']: elements with index in range
a[0...2]           # => ['a','b']: three dots instead of two
a[1,1]             # => ['b']: single element, as an array
a[-2,2]            # => ['c','d']: last two elements
a[4,2]             # => []: empty array right at the end 
a[5,1]             # => nil: nothing beyond that
a.slice(0..1)      # => ['a','b']: slice is synonym for []
a.first(3)         # => ['a','b','c']: first three elements
a.last(1)          # => ['d']: last element as an array

# Extracting arbitrary values
a.values_at(0,2)         # => ['a','c']
a.values_at(4, 3, 2, 1)  # => [nil, 'd','c','b']
a.values_at(0, 2..3, -1) # => ['a','c','d','d']
a.values_at(0..2,1..3)   # => ['a','b','c','b','c','d']

Altering array elements

The following code demonstrates how to change the value of individual array elements, insert values into an array, delete values from an array, and replace values with other values:

a = [1,2,3]        # Start with this array
# Changing the value of elements
a[0] = 0           # Alter an existing element: a is [0,2,3]
a[-1] = 4          # Alter the last element: a is [0,2,4]
a[1] = nil         # Set the 2nd element to nil: a is [0,nil,4]

# Appending to an array
a = [1,2,3]        # Start over with this array
a[3] = 4           # Add a fourth element to it: a is [1,2,3,4]
a[5] = 6           # We can skip elements: a is [1,2,3,4,nil,6]
a << 7             # => [1,2,3,4,nil,6,7]
a << 8 << 9        # => [1,2,3,4,nil,6,7,8,9] operator is chainable
a = [1,2,3]        # Start over with short array
a + a              # => [1,2,3,1,2,3]: + concatenates into new array
a.concat([4,5])    # => [1,2,3,4,5]: alter a in place: note no !

# Inserting elements with insert
a = ['a', 'b', 'c']
a.insert(1, 1, 2)  # a now holds ['a',1,2,'b','c']. Like a[1,0] = [1,2]

# Removing (and returning) individual elements by index
a = [1,2,3,4,5,6]
a.delete_at(4)     # => 5: a is now [1,2,3,4,6]
a.delete_at(-1)    # => 6: a is now [1,2,3,4]
a.delete_at(4)     # => nil: a is unchanged

# Removing elements by value
a.delete(4)        # => 4: a is [1,2,3]
a[1] = 1           # a is now [1,1,3]
a.delete(1)        # => 1: a is now [3]: both 1s removed
a = [1,2,3]
a.delete_if {|x| x%2==1} # Remove odd values: a is now [2]
a.reject! {|x| x%2==0}   # Like delete_if: a is now []
 
# Removing elements and subarrays with slice!
a = [1,2,3,4,5,6,7,8]
a.slice!(0)        # => 1: remove element 0: a is [2,3,4,5,6,7,8]
a.slice!(-1,1)     # => [8]: remove subarray at end: a is [2,3,4,5,6,7]
a.slice!(2..3)     # => [4,5]: works with ranges: a is [2,3,6,7]
a.slice!(4,2)      # => []: empty array just past end: a unchanged
a.slice!(5,2)      # => nil: a now holds [2,3,6,7,nil]!

# Replacing subarrays with []=
# To delete, assign an empty array
# To insert, assign to a zero-width slice
a = ('a'..'e').to_a    # => ['a','b','c','d','e']
a[0,2] = ['A','B']     # a now holds ['A', 'B', 'c', 'd', 'e']
a[2...5]=['C','D','E'] # a now holds ['A', 'B', 'C', 'D', 'E']
a[0,0] = [1,2,3]       # Insert elements at the beginning of a
a[0..2] = []           # Delete those elements
a[-1,1] = ['Z']        # Replace last element with another
a[-1,1] = 'Z'          # For single elements, the array is optional
a[1,4] = nil           # Ruby 1.9: a now holds ['A',nil]
                       # Ruby 1.8: a now holds ['A']: nil works like []

# Other methods
a = [4,5]
a.replace([1,2,3])     # a now holds [1,2,3]: a copy of its argument
a.fill(0)              # a now holds [0,0,0]
a.fill(nil,1,3)        # a now holds [0,nil,nil,nil]
a.fill('a',2..4)       # a now holds [0,nil,'a','a','a']
a[3].upcase!           # a now holds [0,nil,'A','A','A']
a.fill(2..4) { 'b' }   # a now holds [0,nil,'b','b','b']
a[3].upcase!           # a now holds [0,nil,'b','B','b']
a.compact              # => [0,'b','B','b']: copy with nils removed
a.compact!             # Remove nils in place: a now holds [0,'b','B','b']
a.clear                # a now holds []

Iterating, searching, and sorting arrays

Array mixes in the Enumerable module, so all of the Enumerable iterators are available. In addition, the Array class defines some important iterators and related searching and sorting methods of its own. In Ruby 1.9 and 1.8.7, array iterators return an enumerator when invoked without a block:

a = ['a','b','c']
a.each {| elt| print elt }         # The basic each iterator prints "abc"
a.reverse_each {|e| print e}       # Array-specific: prints "cba" 
a.cycle {|e| print e }             # Ruby 1.9, 1.8.7: print "abcabc..." forever
a.each_index {|i| print i}         # Array-specific: prints "012"
a.each_with_index{|e,i| print e,i} # Enumerable: prints "a0b1c2"
a.map {|x| x.upcase}               # Enumerable: returns ['A','B','C']
a.map! {|x| x.upcase}              # Array-specific: alters a in place
a.collect! {|x| x.downcase!}       # collect! is synonym for map!

# Searching methods
a = %w[h e l l o]
a.include?('e')                    # => true
a.include?('w')                    # => false
a.index('l')                       # => 2: index of first match
a.index('L')                       # => nil: no match found
a.rindex('l')                      # => 3: search backwards
a.index {|c| c =~ /[aeiou]/}       # => 1: index of 1st vowel. 1.9 and 1.8.7
a.rindex {|c| c =~ /[aeiou]/}      # => 4: index of last vowel. 1.9 and 1.8.7

# Sorting
a.sort     # => %w[e h l l o]: copy a and sort the copy
a.sort!    # Sort in place: a now holds ['e','h','l','l','o']
a = [1,2,3,4,5]               # A new array to sort into evens and odds
a.sort! {|a,b| a%2 <=> b%2}   # Compare elements modulo 2

# Shuffling arrays: the opposite of sorting; Ruby 1.9 and 1.8.7
a = [1,2,3]     # Start ordered
puts a.shuffle  # Shuffle randomly. E.g.: [3,1,2]. Also shuffle!

Array comparison

Two arrays are equal if and only if they have the same number of elements, the elements have the same values, and they appear in the same order. The == method tests the equality of its elements with ==, and the eql? method tests the equality of its elements by calling eql? on them. In most cases, these two equality-testing methods return the same result.

The Array class is not Comparable, but it does implement the <=> operator and defines an ordering for arrays. This ordering is analogous to string ordering, and arrays of character codes are sorted in the same way that the corresponding String objects are. Arrays are compared element-by-element from index 0. If any pair of elements is not equal, then the array-comparison method returns the same value as the element comparison did. If all pairs of elements are equal, and the two arrays have the same length, then the arrays are equal and <=> returns 0. Otherwise, one of the arrays is a prefix of the other. In this case, the longer array is greater than the shorter array. Note that the empty array [] is a prefix of every other array and is always less than any nonempty array. Also, if a pair of array elements is incomparable (if one is a number and one is a string, for example), then <=> returns nil rather than returning –1, 0, or +1:

[1,2] <=> [4,5]      # => -1 because 1 < 4
[1,2] <=> [0,0,0]    # => +1 because 1 > 0
[1,2] <=> [1,2,3]    # => -1 because first array is shorter
[1,2] <=> [1,2]      # => 0: they are equal
[1,2] <=> []         # => +1 [] always less than a nonempty array

Arrays as stacks and queues

The push and pop add and remove elements from the end of an array. They allow you to use an array as a last-on-first-off stack:

a = []
a.push(1)     # => [1]: a is now [1]
a.push(2,3)   # => [1,2,3]: a is now [1,2,3]
a.pop         # => 3: a is now [1,2]
a.pop         # => 2: a is now [1]
a.pop         # => 1: a is now []
a.pop         # => nil: a is still []

shift is like pop, but it removes and returns the first element of an array instead of the last element. unshift is like push, but it adds elements at the beginning of the array instead of the end. You can use push and shift to implement a first-in-first-out queue:

a = []
a.push(1)     # => [1]: a is [1]
a.push(2)     # => [1,2]: a is [1,2]
a.shift       # => 1: a is [2]
a.push(3)     # => [2,3]: a is [2,3]
a.shift       # => 2: a is [3]
a.shift       # => 3: a is []
a.shift       # => nil: a is []

Arrays as sets

The Array class implements the &, |, and - operators to perform set-like intersection, union, and difference operations. Furthermore, it defines include? to test for the presence (membership) of a value in an array. It even defines uniq and uniq! to remove duplicate values from an array (sets don’t allow duplicates). Array is not an efficient set implementation (for that, see the Set class in the standard library), but it may be convenient to use it to represent small sets:

[1,3,5] & [1,2,3]           # => [1,3]: set intersection
[1,1,3,5] & [1,2,3]         # => [1,3]: duplicates removed
[1,3,5] | [2,4,6]           # => [1,3,5,2,4,6]: set union
[1,3,5,5] | [2,4,6,6]       # => [1,3,5,2,4,6]: duplicates removed
[1,2,3] - [2,3]             # => [1]: set difference
[1,1,2,2,3,3] - [2, 3]      # => [1,1]: not all duplicates removed

small = 0..10.to_a          # A set of small numbers
even = 0..50.map {|x| x*2}  # A set of even numbers
smalleven = small & even    # Set intersection
smalleven.include?(8)       # => true: test for set membership

[1, 1, nil, nil].uniq       # => [1, nil]: remove dups. Also uniq!

Note that the & and | - operators do not specify the order of the elements in the arrays they return. Only use these operators if your array truly represents an unordered set of values.

In Ruby 1.9, the Array class defines set combinatorics methods for computing permutations, combinations, and Cartesian products:

a = [1,2,3]

# Iterate all possible 2-element subarrays (order matters)
a.permutation(2) {|x| print x }  # Prints "[1,2][1,3][2,1][2,3][3,1][3,2]"

# Iterate all possible 2-element subsets (order does not matter)
a.combination(2) {|x| print x }  # Prints "[1, 2][1, 3][2, 3]"

# Return the Cartesian product of the two sets
a.product(['a','b'])       # => [[1,"a"],[1,"b"],[2,"a"],[2,"b"],[3,"a"],[3,"b"]]
[1,2].product([3,4],[5,6]) # => [[1,3,5],[1,3,6],[1,4,5],[1,4,6], etc... ] 

Associative array methods

The assoc and rassoc methods allow you to treat an array as an associative array or hash. For this to work, the array must be an array of arrays, typically like this:

[[key1, value1], [key2, value2], [key3, value3], ...]

The Hash class defines methods that convert a hash to a nested array of this form. The assoc methods looks for a nested array whose first element matches the supplied argument. It returns the first matching nested array. The rassoc method does the same thing, but returns the first nested array whose second element matches:

h = { :a => 1, :b => 2}   # Start with a hash
a = h.to_a                # => [[:b,2], [:a,1]]: associative array
a.assoc(:a)               # => [:a,1]: subarray for key :a
a.assoc(:b).last          # => 2: value for key :b
a.rassoc(1)               # => [:a,1]: subarray for value 1
a.rassoc(2).first         # => :b: key for value 2
a.assoc(:c)               # => nil
a.transpose               # => [[:a, :b], [1, 2]]: swap rows and cols

Miscellaneous array methods

Array defines a few miscellaneous methods that do not fit in any of the previous categories:

# Conversion to strings
[1,2,3].join              # => "123": convert elements to string and join
[1,2,3].join(", ")        # => "1, 2, 3": optional delimiter
[1,2,3].to_s              # => "[1, 2, 3]" in Ruby 1.9
[1,2,3].to_s              # => "123" in Ruby 1.8
[1,2,3].inspect           # => "[1, 2, 3]": better for debugging in 1.8

# Binary conversion with pack. See also String.unpack.
[1,2,3,4].pack("CCCC")    # => "01020304"
[1,2].pack('s2')          # => "01000200"
[1234].pack("i")          # => "322040000"

# Other methods
[0,1]*3                   # => [0,1,0,1,0,1]: * operator repeats
[1, [2, [3]]].flatten     # => [1,2,3]: recursively flatten; also flatten!
[1, [2, [3]]].flatten(1)  # => [1,2,[3]]: specify # of levels; Ruby 1.9
[1,2,3].reverse           # => [3,2,1]: also reverse!
a=[1,2,3].zip([:a,:b,:c]) # => [[1,:a],[2,:b],[3,:c]]: Enumerable method
a.transpose               # => [[1,2,3],[:a,:b,:c]]: swap rows/cols

Hashes

Hashes were introduced in Hashes, which explained hash literal syntax and the [] and []= operators for retrieving and storing key/value pairs in a hash. This section covers the Hash API in more detail. Hashes use the same square-bracket operators as arrays do, and you’ll notice that many Hash methods are similar to Array methods.

Creating hashes

Hashes can be created with literals, the Hash.new method, or the [] operator of the Hash class itself:

{ :one => 1, :two => 2 }  # Basic hash literal syntax
{ :one, 1, :two, 2 }      # Same, with deprecated Ruby 1.8 syntax
{ one: 1, two: 2 }        # Same, Ruby 1.9 syntax. Keys are symbols.
{}                        # A new, empty, Hash object
Hash.new                  # => {}: creates empty hash
Hash[:one, 1, :two, 2]    # => {one:1, two:2}

Recall from Hashes for Named Arguments that you can omit the curly braces around a hash literal that is the final argument in a method invocation:

puts :a=>1, :b=>2   # Curly braces omitted in invocation
puts a:1, b:2       # Ruby 1.9 syntax works too

Indexing hashes and testing membership

Hashes are very efficient at looking up the value associated with a given key. It is also possible (though not efficient) to find a key with which a value is associated. Note, however, that many keys can map to the same value, and in this case, the key returned is arbitrary:

h = { :one => 1, :two => 2 }
h[:one]       # => 1: find value associated with a key
h[:three]     # => nil: the key does not exist in the hash
h.assoc :one  # => [:one, 1]: find key/value pair. Ruby 1.9.

h.index 1     # => :one: search for key associated with a value
h.index 4     # => nil: no mapping to this value exists
h.rassoc 2    # => [:two, 2]: key/value pair matching value. Ruby 1.9.

Hash defines several synonymous methods for testing membership:

h = { :a => 1, :b => 2 }
# Checking for the presence of keys in a hash: fast
h.key?(:a)       # true: :a is a key in h
h.has_key?(:b)   # true: has_key? is a synonym for key?
h.include?(:c)   # false: include? is another synonym
h.member?(:d)    # false: member? is yet another synonym

# Checking for the presence of values: slow
h.value?(1)      # true: 1 is a value in h
h.has_value?(3)  # false: has_value? is a synonym for value?

The fetch method is an alternative to [] when querying values in a hash. It provides options for handling the case where a key does not exist in the hash:

h = { :a => 1, :b => 2 }
h.fetch(:a)      # => 1: works like [] for existing keys
h.fetch(:c)      # Raises IndexError for nonexistent key
h.fetch(:c, 33)  # => 33: uses specified value if key is not found
h.fetch(:c) {|k| k.to_s } # => "c": calls block if key not found

If you want to extract more than one value from a hash at once, use values_at:

h = { :a => 1, :b => 2, :c => 3 }
h.values_at(:c)         # => [3]: values returned in an array
h.values_at(:a, :b)     # => [1, 2]: pass any # of args
h.values_at(:d, :d, :a) # => [nil, nil, 1]

You can extract keys and values selected by a block with the select method:

h = { :a => 1, :b => 2, :c => 3 }
h.select {|k,v| v % 2 == 0 } # => [:b,2] Ruby 1.8
h.select {|k,v| v % 2 == 0 } # => {:b=>2} Ruby 1.9

This method overrides Enumerable.select. In Ruby 1.8, select returns an array of key/value pairs. It has been modified in Ruby 1.9 so that it returns a hash of the selected keys and values instead.

Storing keys and values in a hash

Associate a value with a key in a hash with the []= operator or its synonym, the store method:

h = {}        # Start with an empty hash
h[:a] = 1     # Map :a=>1.  h is now {:a=>1}
h.store(:b,2) # More verbose: h is now {:a=>1, :b=>2}

To replace all the key/value pairs in a hash with copies of the pairs from another hash, use replace:

# Replace all of the pairs in h with those from another hash
h.replace({1=>:a, 2=>:b}) # h is now equal to the argument hash

The merge, merge!, and update methods allow you to merge the mappings from two hashes:

# Merge hashes h and j into new hash k.  
# If h and j share keys, use values from j
k = h.merge(j)
{:a=>1,:b=>2}.merge(:a=>3,:c=>3)  # => {:a=>3,:b=>2,:c=>3}
h.merge!(j)   # Modifies h in place.

# If there is a block, use it to decide which value to use
h.merge!(j) {|key,h,j| h }      # Use value from h
h.merge(j) {|key,h,j| (h+j)/2 } # Use average of two values

# update is a synonym for merge!
h = {a:1,b:2}     # Using Ruby 1.9 syntax and omitting braces
h.update(b:4,c:9) {|key,old,new| old }  # h is now {a:1, b:2, c:9}
h.update(b:4,c:9) # h is now {a:1, b:4, c:9}

Removing hash entries

You can’t remove a key from a hash simply by mapping it to nil. Instead, use the delete method:

h = {:a=>1, :b=>2}
h[:a] = nil      # h now holds {:a=> nil, :b=>2 }
h.include? :a    # => true
h.delete :b      # => 2: returns deleted value: h now holds {:a=>nil}
h.include? :b    # => false
h.delete :b      # => nil: key not found
# Invoke block if key not found
h.delete(:b) {|k| raise IndexError, k.to_s } # IndexError!

You can delete multiple key/value pairs from a hash using the delete_if and reject! iterators (and the reject iterator which operates on a copy of its receiver). Note that reject overrides the Enumerable method by the same name and returns a hash rather than an array:

h = {:a=>1, :b=>2, :c=>3, :d=>"four"}
h.reject! {|k,v| v.is_a? String }  # => {:a=>1, :b=>2, :c=>3 }
h.delete_if {|k,v| k.to_s < 'b' }  # => {:b=>2, :c=>3 }
h.reject! {|k,v| k.to_s < 'b' }    # => nil: no change
h.delete_if {|k,v| k.to_s < 'b' }  # => {:b=>2, :c=>3 }: unchanged hash
h.reject {|k,v| true }             # => {}: h is unchanged

Finally, you can remove all key/value pairs from a hash with the clear method. This method does not end with an exclamation mark, but it alters its receiver in place:

h.clear    # h is now {}

Arrays from hashes

Hash defines methods for extracting hash data into arrays:

h = { :a=>1, :b=>2, :c=>3 }
# Size of hash: number of key/value pairs
h.length     # => 3
h.size       # => 3: size is a synonym for length
h.empty?     # => false
{}.empty?    # => true

h.keys       # => [:b, :c, :a]: array of keys
h.values     # => [2,3,1]: array of values
h.to_a       # => [[:b,2],[:c,3],[:a,1]]: array of pairs
h.flatten    # => [:b, 2, :c, 3, :a, 1]: flattened array. Ruby 1.9
h.sort       # => [[:a,1],[:b,2],[:c,3]]: sorted array of pairs
h.sort {|a,b| a[1]<=>b[1] } # Sort pairs by value instead of key

Hash iterators

It is not usually necessary to extract hash keys, values, or pairs as an array, because the Hash class is Enumerable and defines other useful iterators as well. In Ruby 1.8, Hash objects make no guarantees about the order in which their values are iterated. In Ruby 1.9, however, hash elements are iterated in their insertion order, and that is the order shown in the following examples:

h = { :a=>1, :b=>2, :c=>3 }

# The each() iterator iterates [key,value] pairs
h.each {|pair| print pair }    # Prints "[:a, 1][:b, 2][:c, 3]"

# It also works with two block arguments
h.each do |key, value|                
  print "#{key}:#{value} "     # Prints "a:1 b:2 c:3" 
end

# Iterate over keys or values or both
h.each_key {|k| print k }      # Prints "abc"
h.each_value {|v| print v }    # Prints "123"
h.each_pair {|k,v| print k,v } # Prints "a1b2c3". Like each

The each iterator yields an array containing the key and value. Block invocation syntax allows this array to be automatically expanded into separate key and value parameters. In Ruby 1.8, the each_pair iterator yields the key and value as two separate values (which may have a slight performance advantage). In Ruby 1.9, each_pair is simply a synonym for each.

Although it is not an iterator, the shift method can be used to iterate through the key/value pairs of a hash. Like the array method of the same name, it removes and returns one element (one [key,value] array in this case) from the hash:

h = { :a=> 1, :b=>2 }
print h.shift[1] while not h.empty?   # Prints "12"

Default values

Normally, if you query the value of a key with which no value has been associated, the hash returns nil:

empty = {}
empty["one"]   # nil

You can alter this behavior, however, by specifying a default value for the hash:

empty = Hash.new(-1)   # Specify a default value when creating hash
empty["one"]           # => -1
empty.default = -2     # Change the default value to something else
empty["two"]           # => -2
empty.default          # => -2: return the default value

Instead of providing a single default value, you can provide a block of code to compute values for keys that do not have an associated value:

# If the key is not defined, return the successor of the key.
plus1 = Hash.new {|hash, key| key.succ }
plus1[1]      # 2
plus1["one"]  # "onf": see String.succ
plus1.default_proc  # Returns the Proc that computes defaults
plus1.default(10)   # => 11: default returned for key 10

When using a default block like this, it is common to associate the computed value with the key, so that the computation does not need to be redone if the key is queried again. This is an easy-to-implement form of lazy evaluation (and it explains why the default block is passed the hash object itself along with the key):

# This lazily initialized hash maps integers to their factorials
fact = Hash.new {|h,k| h[k] = if k > 1: k*h[k-1] else 1 end }
fact      # {}: it starts off empty
fact[4]   # 24: 4! is 24
fact      # {1=>1, 2=>2, 3=>6, 4=>24}: the hash now has entries

Note that setting the default property of a hash overrides any block passed to the Hash.new constructor.

If you are not interested in default values for a hash, or if you want to override them with your own default, use the fetch method to retrieve values instead of using square brackets. fetch was covered earlier:

fact.fetch(5)   # IndexError: key not found

Hashcodes, key equality, and mutable keys

In order for an object to be used as a hash key, it must have a hash method that returns an integer “hashcode” for the object. Classes that do not define their own eql? method can simply use the hash method they inherit from Object. If you define an eql? method for testing object equality, however, you must define a corresponding hash method. If two distinct objects are considered equal, their hash methods must return the same value. Ideally, two objects that are not equal should have different hashcodes. This topic was covered in Hash Codes, Equality, and Mutable Keys, and Point Equality includes an example hash implementation.

As noted in Hash Codes, Equality, and Mutable Keys, you must be careful any time you use a mutable object as a hash key. (Strings are a special case: the Hash class makes a private internal copy of string keys.) If you do use mutable keys and mutate one of them, you must call rehash on the Hash object in order to ensure that it works right:

key = {:a=>1}      # This hash will be a key in another hash!
h = { key => 2 }   # This hash has a mutable key
h[key]             # => 2: get value associated with key
key.clear          # Mutate the key
h[key]             # => nil: no value found for mutated key
h.rehash           # Fix up the hash after mutation
h[key]             # => 2: now the value is found again

Miscellaneous hash methods

The invert method does not fit into any of the previous categories. invert swaps keys and values in a hash:

h = {:a=>1, :b=>2}
h.invert        # => {1=>:a, 2=>:b}: swap keys and values

As was the case for Array, the Hash.to_s method is not very useful in Ruby 1.8, and you may prefer to use inspect to convert to a string in hash literal form. In Ruby 1.9, to_s and inspect are the same:

{:a=>1, :b=>2}.to_s    # => "a1b2" in Ruby 1.8; "{:a=>1, :b=>2}" in 1.9
{:a=>1, :b=>2}.inspect # => "{:a=>1, :b=>2}" for both versions

Sets

A set is simply a collection of values, without duplicates. Unlike an array, the elements of a set have no order. A hash can be considered a set of key/value pairs. Conversely, a set can be implemented using a hash in which set elements are stored as keys and values are ignored. A sorted set is a set that imposes an ordering on its elements (but does not allow random access to them as an array does). A characteristic feature of set implementations is that they feature fast membership testing, insertion, and deletion operations.

Ruby does not offer a built-in set type, but the standard library includes the Set and SortedSet classes, which you can use if you first:

require 'set'

The Set API is similar in many ways to the Array and Hash APIs. A number of Set methods and operators accept any Enumerable object as their argument.

Creating sets

Because Set is not a core Ruby class, there is no literal syntax for creating sets. The set library adds a to_set method to the Enumerable module, and a set can be created from any enumerable object with this method:

(1..5).to_set              # => #<Set: {5, 1, 2, 3, 4}>
[1,2,3].to_set             # => #<Set: {1, 2, 3}>

Alternatively, any enumerable object can be passed to Set.new. If a block is provided, it is used (as with the map iterator) to preprocess the enumerated values before adding them to the set:

Set.new(1..5)              # => #<Set: {5, 1, 2, 3, 4}>
Set.new([1,2,3])           # => #<Set: {1, 2, 3}>
Set.new([1,2,3]) {|x| x+1} # => #<Set: {2, 3, 4}>

If you prefer to enumerate the members of your set without first placing them in an array or other enumerable object, use the [] operator of the Set class:

Set["cow", "pig", "hen"]   # => #<Set: {"cow", "pig", "hen"}>

Testing, comparing, and combining Sets

The most common operation on sets is usually membership testing:

s = Set.new(1..3)   # => #<Set: {1, 2, 3}>  
s.include? 1        # => true
s.member? 0         # => false: member? is a synonym

It is also possible to test sets for membership in other sets. A set S is a subset of T if all the elements of S are also elements of T. We can also say that T is a superset of S. If two sets are equal, then they are both subsets and supersets of each other. S is a proper subset of T if it is a subset of T but not equal to T. In this case, T is a proper superset of S:

s = Set[2, 3, 5]
t = Set[2, 3, 5, 7]
s.subset? t            # => true
t.subset? s            # => false
s.proper_subset? t     # => true
t.superset? s          # => true
t.proper_superset? s   # => true
s.subset? s            # => true
s.proper_subset? s     # => false

Set defines the same size methods as Array and Hash do:

s = Set[2, 3, 5]
s.length               # => 3
s.size                 # => 3: a synonym for length
s.empty?               # => false
Set.new.empty?         # => true

New sets can be created by combining two existing sets. There are several ways this can be done, and Set defines the operators &, |, , and ^ (plus named method aliases) to represent them:

# Here are two simple sets
primes = Set[2, 3, 5, 7]
odds = Set[1, 3, 5, 7, 9]

# The intersection is the set of values that appear in both
primes & odds             # => #<Set: {5, 7, 3}>
primes.intersection(odds) # this is an explicitly named alias

# The union is the set of values that appear in either
primes | odds             # => #<Set: {5, 1, 7, 2, 3, 9}>
primes.union(odds)        # an explicitly named alias

# a-b: is the elements of a except for those also in b
primes-odds               # => #<Set: {2}>
odds-primes               # => #<Set: {1, 9}>
primes.difference(odds)   # A named method alias

# a^b is the set of values that appear in one set but not both: (a|b)-(a&b)
primes ^ odds             # => #<Set: {1, 2, 9}>

The Set class also defines mutating variants of some of these methods; we’ll consider them shortly.

Adding and deleting set elements

This section describes methods that add or remove elements from a set. They are mutator methods that modify the receiver set in place rather than returning a modified copy and leaving the original unchanged. Because these methods do not exist in nonmutating versions, they do not have an exclamation point suffix.

The << operator adds a single element to a set:

s = Set[]              # start with an empty set
s << 1                 # => #<Set: {1}>
s.add 2                # => #<Set: {1, 2}>: add is a synonym for <<
s << 3 << 4 << 5       # => #<Set: {5, 1, 2, 3, 4}>: can be chained
s.add 3                # => #<Set: {5, 1, 2, 3, 4}>: value unchanged
s.add? 6               # => #<Set: {5, 6, 1, 2, 3, 4}>
s.add? 3               # => nil: the set was not changed 

To add more than one value to a set, use the merge method, which can take any enumerable object as its argument. merge is effectively a mutating version of the union method:

s = (1..3).to_set   # => #<Set: {1, 2, 3}>
s.merge(2..5)       # => #<Set: {5, 1, 2, 3, 4}>

To remove a single element from a set, use delete or delete?, which are analogous to add and add? but do not have an operator equivalent:

s = (1..3).to_set   # => #<Set: {1, 2, 3}>
s.delete 1          # => #<Set: {2, 3}>
s.delete 1          # => #<Set: {2, 3}>: unchanged
s.delete? 1         # => nil: returns nil when no change
s.delete? 2         # => #<Set: {3}>: otherwise returns set

Remove multiple values from a set at once with subtract. The argument to this method can be any enumerable object, and the method acts as a mutating version of the difference method:

s = (1..3).to_set   # => #<Set: {1, 2, 3}>
s.subtract(2..10)   # => #<Set: {1}>

To selectively delete elements from a set, use delete_if or reject!. Just as with the Array and Hash classes, these two methods are equivalent except for their return value when the set is unmodified. delete_if always returns the receiver set. reject! returns the receiver set if it was modified, or nil if no values were removed from it:

primes = Set[2, 3, 5, 7]       # set of prime numbers
primes.delete_if {|x| x%2==1}  # => #<Set: {2}>: remove odds
primes.delete_if {|x| x%2==1}  # => #<Set: {2}>: unchanged
primes.reject! {|x| x%2==1}    # => nil: unchanged

# Do an in-place intersection like this:
s = (1..5).to_set
t = (4..8).to_set
s.reject! {|x| not t.include? x}  # => #<Set: {5, 4}>

Finally, the clear and replace methods work just as they do for arrays and hashes:

s = Set.new(1..3) # Initial set
s.replace(3..4)   # Replace all elements.  Argument is any enumerable
s.clear           # => #<Set: {}>
s.empty?          # => true

Set iterators

Sets are Enumerable, and the Set class defines an each iterator that yields each of the set elements once. In Ruby 1.9, Set behaves like the Hash class on which it is implemented and iterates elements in the order in which they were inserted. Prior to Ruby 1.9 the iteration order is arbitrary. For SortedSet, the elements are yielded in their ascending sorted order. In addition, the map! iterator transforms each element of the set with a block, altering the set in place. collect! is a synonym:

s = Set[1, 2, 3, 4, 5] # => #<Set: {5, 1, 2, 3, 4}>
s.each {|x| print x }  # prints "51234": arbitrary order before Ruby 1.9
s.map! {|x| x*x }      # => #<Set: {16, 1, 25, 9, 4}>
s.collect! {|x| x/2 }  # => #<Set: {0, 12, 2, 8, 4}>

Miscellaneous set methods

Set defines powerful methods for partitioning sets into subsets and for flattening sets of subsets into single larger sets. In addition, it defines a few mundane methods that we will cover first:

s = (1..3).to_set
s.to_a          # => [1, 2, 3]
s.to_s          # => "#<Set:0xb7e8f938>": not useful
s.inspect       # => "#<Set: {1, 2, 3}>": useful       
s == Set[3,2,1] # => true: uses eql? to compare set elements

The classify method expects a block and yields each set element to that block in turn. The return value is a hash that maps block return values to sets of elements that returned that value:

# Classify set elements as even or odd
s = (0..3).to_set     # => #<Set: {0, 1, 2, 3}>
s.classify {|x| x%2}  # => {0=>#<Set: {0, 2}>, 1=>#<Set: {1, 3}>}

The divide method is similar but returns a set of subsets rather than hash mapping values to subsets:

s.divide {|x| x%2}  # => #<Set: {#<Set: {0, 2}>, #<Set: {1, 3}>}>

divide works completely differently if the associated block expects two arguments. In this case, the block should return true if the two values belong in the same subset, and false otherwise:

s = %w[ant ape cow hen hog].to_set # A set of words
s.divide {|x,y| x[0] == y[0]}      # Divide into subsets by first letter
# => #<Set:{#<Set:{"hog", "hen"}>, #<Set:{"cow"}>, #<Set:{"ape", "ant"}>}>

If you have a set of sets (which may themselves include sets, recursively), you can flatten it, effectively merging (by union) all the contained sets with the flatten method, or the flatten! method, which performs the operation in place:

s = %w[ant ape cow hen hog].to_set # A set of words
t = s.divide {|x,y| x[0] == y[0]}  # Divide it into subsets
t.flatten!                         # Flatten the subsets
t == s                             # => true
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.179.59