10 Collections central: Enumerable and Enumerator
The chapter explains that Ruby’s collection behavior is built around a simple agreement: if a class defines each and includes Enumerable, it automatically gains a large set of powerful querying, filtering, transforming, and traversal methods. This lets arrays, hashes, ranges, and sets share a common interface even though each collection still has its own special rules. The chapter also shows how this pattern can be extended to your own classes, and how Ruby sometimes optimizes or overrides enumerable behavior in specific collection classes when needed.
Much of the chapter is devoted to practical uses of enumerable methods. It covers Boolean checks like all?, any?, none?, and one?; searching and selecting with find, select, reject, and grep; grouping with group_by and partition; and element-focused operations such as first, take, drop, min, and max. It also highlights the difference between methods that return a single matching value, methods that return arrays, and methods whose usefulness depends on whether the underlying collection can actually be traversed in a finite way.
The later part of the chapter broadens the discussion to related iteration tools. It introduces methods that behave like specialized versions of each, including reverse_each, each_with_index, each_slice, each_cons, cycle, and inject, then moves to map as the key transformation method. From there, it explains strings as quasi-enumerable sequences of bytes, characters, code points, or lines, and then develops Enumerator as an object that stores iteration logic, supports chaining, protects collections from mutation, and even makes non-enumerable objects feel enumerable. The chapter ends by showing how lazy enumerators make it possible to work with infinite collections while still producing finite, useful results.
Summary
- The Enumerable module and Enumerator class are two entities with close ties.
- Enumerable methods are defined in terms of each and become available when a class mixes in Enumerable and responds to each.
- Enumerators encapsulate iteration by binding their each method to a designated method on another object, providing Enumerable functionality.
- Enumerators are not general-purpose proxies to another object; they siphon off values from one method on the other object.
- Enumerators can be initialized by “loading” a Yielder object with the values to be provided during iteration.
The Well-Grounded Rubyist, Fourth Edition ebook for free