← 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.
- Ordered sequences. Array is an ordered, integer-indexed collection of objects.[2] Range represents a collection of values between given begin and end values.[5] Position determines identity.
- Keyed maps. Hash maps each of its unique keys to a specific value.[3] Lookup is by key rather than by numeric position.
- Unordered unique collections. Set implements a collection of unordered values with no duplicates, described in the documentation as a hybrid of Array's inter-operation facilities and Hash's fast lookup.[4]
Fundamental constraints
Four properties constrain how these structures behave and how code that uses them must be written.
- Ordering. Array and Range are positional. Hash presents its entries in the order of their creation. Set is unordered.[2][3][4][5]
- Uniqueness. Array permits duplicate elements, Hash enforces unique keys, and Set enforces unique values.[2][3][4]
- Key equivalence. Two objects are treated as the same Hash key when their
hashvalues are identical and the two objects areeql?to each other.[3] - Evaluation time. Most Enumerable methods evaluate eagerly and return an Array or Hash. Enumerator::Lazy defers evaluation, constructing chains of operations without evaluating them immediately and producing values on an as-needed basis.[7]
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.
- Querying:
include?andmember?,all?,any?,none?,one?,count,tally. - Fetching:
entriesandto_a,first,take,drop,take_while,drop_while,min,max,minmax,min_by,max_by. - Searching and filtering:
findanddetect,find_all,select, andfilter,find_index,reject,uniq. - Sorting:
sort,sort_by. - Iterating:
each_entry,each_with_index,each_with_object,each_slice,each_cons,reverse_each. - Transforming and combining:
mapandcollect,filter_map,flat_mapandcollect_concat,grep,grep_v,reduceandinject,sum,zip,cycle.
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.
| Structure | Ordering | Uniqueness | Access | Typical use |
|---|---|---|---|---|
| Array | Positional | Duplicates allowed | Integer index | Sequences, stacks, queues |
| Hash | Insertion order | Unique keys | Key | Mappings, counts, lookups |
| Set | Unordered | Unique values | Membership | Membership tests, deduplication |
| Range | Positional | Not applicable | Position or cover? | Contiguous spans, intervals |
| Struct | Member order | Not applicable | Named member | Fixed 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.
| Operation | Array | Hash | Set |
|---|---|---|---|
| Access by position or key | \(O(1)\) | expected \(O(1)\) | not applicable |
| Membership test | \(O(n)\) | expected \(O(1)\) | expected \(O(1)\) |
| Append | amortized \(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
- ^ Ruby Documentation, "Module Enumerable," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Enumerable.html
- ^ Ruby Documentation, "Class Array," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Array.html
- ^ Ruby Documentation, "Class Hash," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Hash.html
- ^ Ruby Documentation, "Class Set," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Set.html
- ^ Ruby Documentation, "Class Range," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Range.html
- ^ Ruby Documentation, "Class Struct," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Struct.html
- ^ 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
- ^ Ruby Documentation, "Module Comparable," Ruby 3.3.0. Free full text: docs.ruby-lang.org/en/3.3/Comparable.html