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.
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.
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_slice
and 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 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.
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']
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.
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
.
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 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.
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.
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']
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 []
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!
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
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 []
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... ]
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
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") # => " 01 02 03 04" [1,2].pack('s2') # => " 01 00 02 00" [1234].pack("i") # => "322 04 00 00" # 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 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.
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
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.
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}
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 {}
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
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"
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
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
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
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.
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"}>
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.
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
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}>
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
3.145.179.59