skip to content

Enumerable Methods

Enumerable's iterators, predicates, map and reduce, sorting and Enumerators, plus making your own class enumerable. Idiomatic Ruby is a chain of these calls, so interviewers read yours closely.

on this pageshow

explore

questions

page 1 of 2

In Ruby, what do each, map and other Enumerable methods return when called without a block, and how is that used?

level: juniorimportance: must knowfreq 55%

answer

  1. an object, not an error
  2. remembers receiver, method and arguments
  3. chain a second iterator onto it
  4. the first method decides the result
  5. size without iterating, or nil

basics

~20 s

Most iterating methods return an Enumerator when no block is given: an object that remembers the receiver, the method and its arguments and runs them later. You chain it (each_with_index.map), iterate it elsewhere, or pull values with next.

solid answer

~40 s

Called without a block, `each`, `map`, `select`, `each_slice(2)`, `each_with_index`, `times`, `each_char` and most other iterators return an `Enumerator` instead of raising. The Enumerator stores the receiver, the method name and its arguments, and calls that method only when someone iterates it. That is what makes chains such as `ticks.each_with_index.map { |t, i| ... }` or `ticks.each_slice(100).map { |batch| ... }` work: the second method's block receives what the first method yields, and the **first** method decides the final return value (`map.with_index` returns the mapped Array, `select.with_index` the selected elements). An Enumerator can also report `size` without iterating when the method knows it (`[1, 2, 3].each.size` is 3, `loop.size` is `Infinity`), or `nil` when it cannot.

code

ruby · 11 lines
ruby
Tick = Data.define(:symbol, :price)
ticks = [Tick.new("ACME", 101), Tick.new("INIT", 99), Tick.new("ACME", 103)]

e = ticks.each_with_index          # no block: an Enumerator
p e.size                            # => 3, computed without iterating

p e.map { |t, i| "#{i}:#{t.symbol}" }
# => ["0:ACME", "1:INIT", "2:ACME"]

p ticks.each_slice(2).map { |batch| batch.sum { |t| t.price } }
# => [200, 103]

go deeper

for a junior

Recall that each, map and select return an Enumerator without a block, and that you can chain another iterator such as map onto it.

for a middle

Explain what an Enumerator stores, why the first method decides the chain's return value, and how size is answered without iterating.

for a senior

Use blockless iterators to expose iteration as a value in APIs, and know which consumers are safe on an endless source.

for a principal

Prefer returning Enumerators from library APIs so callers choose eager, lazy or external iteration without extra API surface.

## An Enumerator instead of an error In Ruby, a method that iterates with a block usually has a second, blockless form. Calling it without a block returns an **`Enumerator`**: ```ruby [1, 2, 3].each # => #<Enumerator: [1, 2, 3]:each> [1, 2, 3].map # => #<Enumerator: [1, 2, 3]:map> 5.times # => #<Enumerator: 5:times> "abc".each_char # => #<Enumerator: "abc":each_char> ``` An Enumerator is a small object holding three things: - the **receiver** (the array, the string, the integer); - the **method name** to call on it (`:each`, `:map`, `:each_slice`); - the **arguments** for that method (for example `2` in `each_slice(2)`). Nothing is iterated when the Enumerator is created. The method runs only when the Enumerator is iterated, and it runs **again** every time the Enumerator is iterated; an Enumerator is a recipe, not a snapshot. Core methods written in C produce these through the `RETURN_SIZED_ENUMERATOR` macro in `enum.c` and `array.c`; your own methods do it with `to_enum`/`enum_for`. ## Chaining iterators Because an Enumerator includes `Enumerable`, you can call another iterating method on it. The inner method's yields become the outer block's arguments: ```ruby %w[a b c].each_with_index.map { |s, i| s * (i + 1) } # => ["a", "bb", "ccc"] (1..7).each_slice(3).map { |batch| batch.sum } # => [6, 15, 7] ``` A rule worth stating in an interview: **the method that created the Enumerator decides what the chain returns**, because the chained block's result is fed back to it. | Chain | Block result used as | Return value | |---|---|---| | `each.with_index { ... }` | ignored, like `each` | the receiver | | `map.with_index { ... }` | the new element | a mapped Array | | `select.with_index { ... }` | a keep/drop verdict | the kept elements | | `each_with_index.map { ... }` | the new element | a mapped Array | ## Size without iterating `Enumerator#size` asks the method for a size **without** running the iteration, when the method can compute one: 1. `[1, 2, 3].each.size` returns `3`. 2. `(1..3).each_slice(2).size` returns `2`. 3. `loop.size` returns `Float::INFINITY`. 4. `(1..100).drop_while.size` returns `nil`, because the answer depends on the block. The rdoc treats `size` as a **hint**: an Enumerator built with an explicit size is not checked against what it yields. ## Where the stock-tick feed uses this A tick feed object that yields ticks from `each_tick` can hand callers an Enumerator instead of a callback API: ```ruby ticks = feed.each_tick # no block: an Enumerator ticks.first(500) # stops after 500 ticks ticks.find { |t| t.price > 100 } # stops at the first match ``` `first` and `find` stop iterating as soon as they have an answer, so a caller can consume only what it needs. For an endless feed, only such short-circuiting methods, `first(n)`, external `next`, or a `lazy` chain are safe; `map` or `to_a` would never return. ## Common misunderstandings - **"map without a block raises."** It returns an Enumerator. The exception is `Enumerator::Lazy`: `[1, 2, 3].lazy.map` raises `ArgumentError` (`tried to call lazy map without a block`). - **"The Enumerator holds a copy of the data."** It holds a reference to the receiver; mutate the array and the next iteration sees the change. - **"Every method has a blockless form."** Methods whose result is a single value computed from a block argument, such as `sum` or `count`, just compute the value; they do not return an Enumerator. ## Summary A blockless iterator returns an `Enumerator` that remembers what to call. Use it to chain a second iterator, to pass iteration around as a value, to ask for `size` cheaply, or to iterate externally with `next`.

  • Why does map.with_index return an Array while each.with_index returns the receiver?
    The chained block's return value is handed back to the method that created the Enumerator. `map` collects those values into a new Array; `each` ignores them and returns its receiver. The Enumerator only relays yields and results between the two.
  • If you build e = ticks.each and then push a tick onto the array, does e see it?
    Yes. The Enumerator holds a reference to the array and calls `each` on it each time it is iterated, so a later iteration sees the appended tick. It is a recipe for iteration, not a copy.

saying these in an interview costs you the question

  • Calling map without a block raises ArgumentError
  • An Enumerator stores a copy of the collection's elements
  • each.with_index returns a new Array like map does
  • Enumerator#size iterates the whole collection to count it
  • An Enumerator can be iterated only once
open as a page

In Ruby, what does Array#each return when called with a block, and why does assigning that result not collect the block's values?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Array#each with a block returns the receiver itself, the very same array object, and throws away whatever the block returns. Assigning its result gives you the original array back, never the block's values; map is the method that collects them.

open as a page

In Ruby, how do you number marathon finishers from 1 while iterating, and why does each_with_index(1) not do it?

level: juniorimportance: must knowfreq 62%

basics

~10 s

Call each.with_index(1): Enumerator#with_index takes a starting offset. each_with_index always counts from 0 and forwards its arguments to each, so on an Array each_with_index(1) raises ArgumentError instead of shifting the index.

open as a page

In Ruby, how do find and select differ when looking for overdue invoices, and what does each return when nothing matches?

level: juniorimportance: must knowfreq 68%

basics

~20 s

find (alias detect) returns the first element whose block is truthy and stops there, or nil when none matches. select (alias filter) walks the whole collection and returns an array of every match, empty when none.

open as a page

In Ruby, how do map and flat_map differ when each franchise region returns an array of store totals?

level: juniorimportance: must knowfreq 60%

basics

~20 s

map returns a new array with one block result per element, so array results stay nested. flat_map concatenates array results one level into a single flat array, keeps non-array results unchanged, and never flattens deeper, unlike map followed by flatten.

open as a page

In Ruby, what must a Playlist class define to include Enumerable, and which methods does it get for free?

level: middleimportance: must knowfreq 55%

basics

~20 s

Define each so it yields every element, then include Enumerable. Every Enumerable method (map, select, reject, find, include?, reduce, sort_by, min_by, first, to_a, each_slice, group_by, tally, lazy) is built on that each; collection results come back as Arrays.

open as a page

In Ruby, why does `rows.inject({}) { |h, r| h[r.bib] = r.secs }` fail, and how does each_with_object avoid it?

level: middleimportance: must knowfreq 55%

basics

~20 s

inject passes the block's return value on as the next memo, and h[k] = v evaluates to v, so the hash is lost after the first row. each_with_object passes the same object every time, ignores the block's value and returns that object.

open as a page

In Ruby, what do any?, all?, none? and one? return on an empty collection, and what changes when you pass them a pattern?

level: middleimportance: must knowfreq 55%

basics

~20 s

On an empty collection all? and none? return true; any? and one? return false. A pattern argument tests pattern === element, so classes, ranges and regexps work, and a block passed alongside is ignored with a warning.

open as a page

In Ruby, how do you rank hotels by rating descending and then price ascending using sort_by or sort?

level: middleimportance: must knowfreq 58%

basics

~20 s

Return an Array key, hotels.sort_by { |h| [-h.rating, h.price] }, because Array#<=> compares element by element. Negation only reverses numeric keys; for a descending String key use sort with (b.x <=> a.x).nonzero? || a.y <=> b.y.

open as a page

In Ruby, how does Enumerable#sort with a <=> block differ from sort_by, and which one is faster?

level: middleimportance: must knowfreq 62%

basics

~20 s

sort's block compares two elements and runs once per comparison; sort_by's block maps each element to a key once, then Ruby sorts the cached keys. sort_by wins when keys are expensive; blockless sort wins when elements are their own keys.

open as a page

In Ruby, how do inject's symbol, block and initial-value forms work, and why does `sales.inject { |sum, s| sum + s.amount }` fail?

level: middleimportance: must knowfreq 58%

basics

~20 s

Without an initial value, inject uses the first element as the memo, so the first call computes Sale + Integer and raises NoMethodError. inject(0) { ... } fixes it; inject(:+) and inject(0, :+) name a method instead of a block.

open as a page

In Ruby, which Enumerator::Lazy calls stay lazy and which force evaluation, and when do you need force, first or eager?

level: seniorimportance: must knowfreq 45%

basics

~20 s

On Enumerator::Lazy, map, select, reject, take, take_while and similar return another Lazy and compute nothing; non-redefined methods such as first(n), to_a/force, find and include? run the chain. eager turns it back into a plain Enumerator.

open as a page

In Ruby, why does playlist.max or playlist.sort raise ArgumentError on an Enumerable Playlist of Track objects, and what fixes it?

level: juniorimportance: should knowfreq 40%

basics

~20 s

sort, min, max and minmax compare the elements with <=>, and a plain Track inherits Object#<=>, which returns nil for different objects, so the call raises ArgumentError. Define <=> on Track, or pass a key with sort_by or max_by.

open as a page

In Ruby, what do Enumerable#min_by, max_by and minmax_by return, and why prefer them over sort_by followed by first?

level: juniorimportance: should knowfreq 52%

basics

~20 s

min_by and max_by return the element with the smallest or largest block value, or nil when empty; with a count n they return an Array. minmax_by returns [min, max] in one pass. None sorts the whole collection.

open as a page

In Ruby, how do Enumerator#next, peek and rewind behave, and what happens when an enumerator runs out of values?

level: middleimportance: should knowfreq 35%

basics

~10 s

next returns the next value and advances; peek returns it without advancing; both raise StopIteration at the end, and keep raising until rewind resets the position. StopIteration#result holds the underlying method's return value.

open as a page

In Ruby, how do you make your own iterator method return an Enumerator when called without a block, using to_enum or enum_for?

level: middleimportance: should knowfreq 38%

basics

~20 s

Start the method with return enum_for(method, args) unless block_given?. The Enumerator re-calls the method with the same arguments when iterated; an optional block to enum_for computes size. Without the guard, yield with no block raises LocalJumpError.

open as a page

In Ruby, how do each_slice and each_cons differ when walking a marathon results table, and what do they return?

level: middleimportance: should knowfreq 42%

basics

~10 s

each_slice(n) yields disjoint groups of n, with a shorter last group; each_cons(n) yields overlapping windows of n consecutive elements, sliding by one. Both return the receiver in Ruby 4.0; before 3.1 they returned nil.

open as a page

In Ruby, how do grep and grep_v filter an invoice list by class, range or regexp, and what does grep with a block return?

level: middleimportance: should knowfreq 32%

basics

~20 s

grep(pattern) returns the elements for which pattern === element, and grep_v returns the rest. The pattern can be a class, range, regexp or plain value. With a block, grep returns the block's results for the matching elements, filtering and mapping in one pass.

open as a page

In Ruby, when do partition and filter_map beat chaining select with reject or map, and which values does filter_map drop?

level: middleimportance: should knowfreq 38%

basics

~20 s

partition splits a collection in one pass into [matching, rest]. filter_map runs a block once per element and keeps its truthy results, dropping both nil and false, so it replaces a map followed by compact or a select followed by map.

open as a page

In Ruby, how does uniq with a block decide which elements are duplicates, and which element does it keep for each key?

level: middleimportance: should knowfreq 40%

basics

~20 s

uniq with a block runs it once per element and treats elements whose block values match as Hash keys as duplicates, keeping the first one seen, in original order. Sort first to choose which element survives.

open as a page

In Ruby, what do min, max and minmax return on an empty list, with a count argument, and with a one-parameter block?

level: middleimportance: should knowfreq 36%

basics

~20 s

On an empty list min and max return nil and minmax returns [nil, nil]. With a count, min(n) and max(n) return arrays, largest first for max. A block must compare two elements like <=>; a one-parameter block silently returns the wrong element.

open as a page

In Ruby, when do you use tally, group_by or to_h with a block to turn a list of franchise sales into a hash?

level: middleimportance: should knowfreq 50%

basics

~20 s

tally counts equal elements into element => count; group_by collects elements under the block's key into key => array; to_h with a block builds one key-value pair per element, where a repeated key silently keeps the last value.

open as a page

In a Ruby class that includes Enumerable, what should each return with and without a block, and why does it matter?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Without a block, each should return an Enumerator (return enum_for(:each) unless block_given?); with a block it should return self, like Array#each. That keeps next, each.with_index and chaining working and avoids leaking the internal Array.

open as a page

In Ruby, why is a Playlist that includes Enumerable usually a better design than class Playlist < Array?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Subclassing Array exposes every Array mutator, so rules like a track limit are easy to bypass, and many Array methods return plain Arrays, more of them since Ruby 3.0. Wrapping an Array and including Enumerable exposes only the API you choose.

open as a page

In Ruby, why do Enumerable#reverse_each and Enumerable#cycle buffer every element, and when do Array and Range avoid that?

level: seniorimportance: should knowfreq 22%

basics

~20 s

A plain Enumerable only offers a forward, one-pass each, so reverse_each first collects everything with to_a and cycle records each element to replay later. Array walks its own indexes, and integer Ranges count down, so neither buffers.

open as a page

In Ruby, scanning a due-date-sorted list of millions of invoices, which query methods stop early, and why is take_while not a drop-in for select?

level: seniorimportance: should knowfreq 28%

basics

~20 s

find, any?, all?, none?, include? and take_while stop once the answer is known; select, reject, count, grep, partition and filter_map walk everything. take_while stops at the first failure, so it matches select only on data ordered by that test.

open as a page

In Ruby, are Array#sort and sort_by stable, and how do you keep tied hotels in their original order?

level: seniorimportance: should knowfreq 32%

basics

~20 s

No. Ruby documents the order of equal elements as indeterminate and possibly unstable, and it varies by platform and input. To keep input order for ties, make the index the last key: sort_by.with_index { |h, i| [-h.rating, i] }.

open as a page

In Ruby, a franchise report totals Float sale amounts with inject(:+) in one service and sum in another, and the totals differ in the last digits; why, and what do you change?

level: seniorimportance: should knowfreq 35%

basics

~20 s

sum adds Floats with Kahan-Babuska compensated summation, which tracks and corrects rounding error; inject(:+) adds naively, so errors accumulate. Use sum everywhere for consistency, and store money as Integer cents or Rational so no Float rounding enters the total.

open as a page

In Ruby, when a custom each yields two values, such as yield track, position, what do map, select and each_entry see?

level: middleimportance: nice to knowfreq 12%

basics

~20 s

Enumerable methods disagree: map { |x| } passes both values, so a one-parameter block gets only the first, while select, to_a and each_entry pack them into [track, position]. each_entry always yields one object per step.

open as a page

In Ruby, how would you pair each month's franchise sales with the same month last year and group consecutive months of growth?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

this_year.zip(last_year) pairs values by position, keeping the receiver's length and padding a shorter argument with nil. chunk_while then groups adjacent elements while its two-element block stays true; slice_when is the same but splits where its block is true.

open as a page

showing 1–30 of 31