← Reference · Nestor G Pestelos Jr · Print this page

Programming Languages · Ruby

Ruby Enumerable Structures

Reference entry · last updated September 19, 2026

Ruby enumerable structures are the collection classes and modules that include the Enumerable mixin, a shared interface of iteration, query, and transformation methods built on one required method, each. The Ruby core classes Array, Hash, Range, Struct, and Enumerator include or extend it, and the standard library adds Set and the CSV classes.[1] Enumerable turns a minimal protocol, the ability to yield successive elements, into dozens of methods for filtering, mapping, sorting, grouping, and reducing a collection.

First principles and definitions

The each protocol

Enumerable is a module rather than a class.[1] A collection class includes it and defines one method, each, which yields successive elements to a block. The Ruby documentation states that virtually all methods in Enumerable call #each in the including class, so the behavior of the whole interface follows from that single method.

The yielded value depends on the class. Hash#each yields the next key-value pair as a two-element Array, and Struct#each yields the next name-value pair as a two-element Array. For the other classes that include the module, each yields the next object from the collection.[1]

class Foo
  include Enumerable

  def each
    yield 1
    yield 1, 2
    yield
  end
end

Foo.new.each_entry { |element| p element }
# 1
# [1, 2]
# nil

An Enumerable method iterates by calling each and consuming what it yields, so the mixin works on any object that supplies the method, regardless of how that object stores its data.

Structure taxonomy

The structures divide by how they order elements and how they identify them.

Fundamental constraints

Four properties constrain how these structures behave and how code that uses them must be written.

Array: ordered sequences

Array is an ordered, integer-indexed collection of objects. Any object, including another array, may be an element, and an array can contain objects of different types. Arrays keep track of their own length at all times.[2]

Indexing and slicing

Indexing starts at 0. A positive index counts from the first element and a negative index counts backward from the end, so -1 is the last element. An out-of-range index returns nil from [], while fetch raises IndexError or returns a supplied default.[2]

arr = [1, 2, 3, 4, 5, 6]
arr[2]       # => 3
arr[100]     # => nil
arr[-3]      # => 4
arr[2, 3]    # => [3, 4, 5]
arr[1..-3]   # => [2, 3, 4]
arr.fetch(100, "oops") # => "oops"

The first and last methods return leading and trailing elements, take(n) returns the first n elements, and drop(n) returns the elements after the first n.[2]

Mutation and selection

Elements are appended with push or <<, prepended with unshift, and inserted at an offset with insert. The pop and shift methods remove and return the last and first elements. Selection methods return new arrays without modifying the receiver, and the destructive variants modify the receiver in place.[2]

arr = [1, 2, 3]
arr.push(4)        # => [1, 2, 3, 4]
arr << 5           # => [1, 2, 3, 4, 5]
arr.unshift(0)     # => [0, 1, 2, 3, 4, 5]

arr = ['foo', 0, nil, 'bar']
arr.compact        # => ['foo', 0, 'bar']; arr unchanged
arr.compact!       # => ['foo', 0, 'bar']; arr changed

select and reject return a new array of matching and non-matching elements. select! and reject! perform the same filtering in place. uniq removes duplicate elements and uniq! does so destructively.[2]

References and default fill

Array.new(3, true) returns an array whose three elements are references to the same object. The documentation recommends that form only when the shared object is natively immutable, such as a symbol, a numeric value, nil, true, or false. The block form, Array.new(4) { Hash.new }, calls the block once per index and is safe for mutable values, because each element is a separate object.[2]

Hash: key-value maps

Hash maps each of its unique keys to a specific value. An Array index is always an integer, while a Hash key can be almost any object. Hash includes Enumerable, so the shared methods apply to its entries.[1][3] Hash also supplies value-side and key-side transforms, transform_values and transform_keys, which rebuild the map from one component of each entry.

Entry order

A Hash presents its entries in the order of their creation. Iterative methods such as each, each_key, and each_value observe that order, as do order-sensitive methods such as shift, keys, values, and inspect. Adding an entry appends it, updating a value leaves the order unchanged, and deleting an entry then re-creating it appends the new entry at the end.[3]

h = {foo: 0, bar: 1}
h[:baz] = 2   # => {:foo=>0, :bar=>1, :baz=>2}
h[:foo] = 3   # => {:foo=>3, :bar=>1, :baz=>2}
h.delete(:foo)
h[:foo] = 5   # => {:bar=>1, :baz=>2, :foo=>5}

Key equivalence and mutation

To be usable as a key, an object implements hash and eql?. Modifying a key while it is in use damages the hash's index, and rehash rebuilds it. An unfrozen String passed as a key is replaced by a duplicated and frozen String, which makes String keys safe by construction. compare_by_identity switches comparison from hash and eql? to object identity.[3]

a0 = [:foo, :bar]
h = {a0 => 0}
h.include?(a0)  # => true
a0[0] = :bam
h.include?(a0)  # => false
h.rehash
h.include?(a0)  # => true

Default values and procs

When a key is not found, [], values_at, and dig return the hash's default value or the result of its default proc. The default value is returned without being duplicated, so a mutable default object is shared across missing keys and the documentation advises against using one. A default proc receives the hash and the missing key, and it may create the entry. A default proc that modifies the hash is not thread-safe when multiple threads call it concurrently for the same key.[3]

Shared Enumerable methods

Because every including class supplies each, the Enumerable interface is the same across Array, Hash, Set, Range, Struct, and Enumerator.[1] The documentation groups the methods by purpose.

Several of these names are aliases for the same method: collect for map, inject for reduce, filter and find_all for select, and detect for find.[1] The filtering, grouping, and reducing protocols are covered together in Filter, Group, and Reduce.

Deferred evaluation

Enumerator::Lazy is a special kind of Enumerator that redefines most Enumerable methods so that each call constructs another lazy enumerator instead of evaluating immediately. The chain is evaluated on an as-needed basis, and a lazy enumerator can be built from an infinite range because values are produced only when requested.[7]

lazy = (1..Float::INFINITY).lazy
                    .select(&:odd?)
                    .drop(10)
                    .take_while { |i| i < 30 }

Specialized enumerable structures

Set

Set implements a collection of unordered values with no duplicates, described in the documentation as a hybrid of Array's intuitive inter-operation facilities and Hash's fast lookup. It includes Enumerable and belongs to the standard library. The method to_set is added to Enumerable for convenience.[1][4]

Range

Range represents a collection of values between given begin and end values. The .. form includes the end value and the ... form excludes it. A Range includes Enumerable, so integer and string ranges enumerate in order.[5]

Struct

Struct provides a convenient way to create a simple class that can store and fetch values under named members. Its each yields each name-value pair as a two-element Array, so a struct participates in Enumerable like any other collection.[1][6]

Enumerator and Comparable

Enumerator is a class that allows both internal and external iteration. In internal iteration the collection drives the loop through a block, and in external iteration the caller advances the sequence with next and peek. An Enumerator includes Enumerable and can wrap any object that responds to each, through to_enum, enum_for, or Enumerator.new.[7]

Comparable is a related mixin for classes whose objects may be ordered. A class defines the <=> operator, and Comparable uses it to supply <, <=, ==, >=, >, and between?.[8] The Enumerable methods sort and max rely on <=> when no block is given.[1]

Choosing a structure

The choice follows from ordering, uniqueness, and the access pattern.

StructureOrderingUniquenessAccessTypical use
ArrayPositionalDuplicates allowedInteger indexSequences, stacks, queues
HashInsertion orderUnique keysKeyMappings, counts, lookups
SetUnorderedUnique valuesMembershipMembership tests, deduplication
RangePositionalNot applicablePosition or cover?Contiguous spans, intervals
StructMember orderNot applicableNamed memberFixed records with named fields

The cost figures below describe the expected behavior of the underlying data structures. They are the conventional costs of arrays, hash tables, and hash-backed sets, not asymptotic guarantees published in the Ruby documentation.

OperationArrayHashSet
Access by position or key\(O(1)\)expected \(O(1)\)not applicable
Membership test\(O(n)\)expected \(O(1)\)expected \(O(1)\)
Appendamortized \(O(1)\)amortized \(O(1)\)amortized \(O(1)\)
Delete by position or key\(O(n)\)expected \(O(1)\)expected \(O(1)\)
Ordered iteration\(O(n)\)\(O(n)\)\(O(n)\)

Array suits ordered sequences, positional access, and stack or queue use. Hash suits keyed lookup, counting, and any mapping from a small set of identifying objects to values. Set suits membership tests and deduplication where order is irrelevant. Struct suits fixed records with named fields, and Enumerator::Lazy suits chains over large or unbounded sequences where eager evaluation would waste work.

See also

References

  1. ^ Ruby Documentation, "Module Enumerable," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Enumerable.html
  2. ^ Ruby Documentation, "Class Array," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Array.html
  3. ^ Ruby Documentation, "Class Hash," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Hash.html
  4. ^ Ruby Documentation, "Class Set," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Set.html
  5. ^ Ruby Documentation, "Class Range," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Range.html
  6. ^ Ruby Documentation, "Class Struct," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Struct.html
  7. ^ Ruby Documentation, "Class Enumerator" and "Enumerator::Lazy," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Enumerator.html and docs.ruby-lang.org/en/3.3/Enumerator/Lazy.html
  8. ^ Ruby Documentation, "Module Comparable," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Comparable.html