In Ruby, what does Array#each return when called with a block, and why does assigning that result not collect the block's values?
answer
- iteration for side effects
- block's value is thrown away
- returns self, the receiver
- same object: equal? is true
- collecting is map's job
basics
~20 sArray#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.
solid answer
~40 s`each` is Ruby's side-effect iterator: it yields every element to the block and, when it finishes, returns **`self`**, the receiver. The block's return value is discarded on every call. So `result = finishers.each { |r| r.upcase }` leaves `result` pointing at the same `Array` as `finishers` (`result.equal?(finishers)` is `true`), not at an array of upcased names; that is what `map` is for. `Hash#each` returns the hash and `Range#each` returns the range, following the same rule. Returning the receiver is deliberate: a method whose last line is `rows.each { ... }` hands the collection back to its caller, and you can keep chaining on it. The trap is aliasing: because it is the same object, pushing onto `result` changes `finishers` too.
code
ruby · 13 linesfinishers = ["Abebe", "Chen", "Okafor"]
result = finishers.each { |name| "#{name} finished" }
result # => ["Abebe", "Chen", "Okafor"]
result.equal?(finishers) # => true, the same Array object
result << "Silva"
finishers # => ["Abebe", "Chen", "Okafor", "Silva"]
labels = finishers.map { |name| "#{name} finished" }
labels.first # => "Abebe finished"
{101 => 7530}.each { |bib, secs| secs * 2 } # => {101 => 7530}go deeper
Recall that each returns the receiver and ignores what the block returns, and that map is the method that collects block results.
Explain that the returned object is the same array, not a copy, so mutating the result mutates the original, and that each re-reads the length while iterating.
Spot methods that return the result of a trailing each by accident, and treat mutation during Array#each as a review finding rather than a style nit.
Frame iteration methods by what they return: side-effect walkers return the receiver, builders return new objects, and a codebase stays readable when each is used only for side effects.
## What `each` is for In Ruby, **`each`** is the basic iterator that every core collection defines. `Array#each`, `Hash#each` and `Range#each` walk their elements in order and pass each one to the **block**, the `{ ... }` or `do ... end` chunk of code attached to the call. `each` exists for **side effects**: printing a line, writing a row, sending a message. It is the method the `Enumerable` module builds everything else on top of. The question interviewers ask is not how to call it but what comes back when it finishes. ## What it returns With a block, `each` returns **`self`**, the object you called it on. The array's rdoc in the Ruby source states the signature as `each {|element| ... } -> self`, and the C implementation ends with `return ary`. - `Array#each` returns the array. - `Hash#each` (an alias of `each_pair`) returns the hash. - `Range#each` returns the range. - Called **without** a block, `each` returns an `Enumerator` instead; that is a separate topic. The block's own return value is **discarded** on every iteration. Ruby evaluates it and moves on. ## The classic bug A marathon results page needs labels for each finisher. A newcomer writes: ```ruby labels = finishers.each { |name| "#{name} finished" } ``` and expects an array of strings. What they get is `finishers` itself. Three things are now true: 1. `labels == finishers` is `true`, because they hold the same names. 2. `labels.equal?(finishers)` is also `true`: it is the **same object**, not a copy. 3. `labels << "extra"` silently adds a row to `finishers`. The fix is to use the method whose job is to **collect** block results, `map`. `each` is only the right tool when nothing needs to be gathered, or when you gather explicitly into another object. ## Why returning `self` is useful Returning the receiver is not an accident; it makes `each` compose well: - A method whose last expression is `rows.each { |r| puts r }` returns `rows`, so the caller can keep working with the collection. - You can chain another call onto the end: `rows.each { |r| log(r) }.size`. - Most of the `each` family follows the same rule, so you can predict the return value without looking it up. | Method (with a block) | Returns | |---|---| | `each` | the receiver | | `each_with_index` | the receiver | | `reverse_each` | the receiver | | `each_slice(n)`, `each_cons(n)` | the receiver (since Ruby 3.1) | | `each_with_object(memo)` | the memo object | | `cycle` | `nil` | | `map` | a new array of block results | ## Modifying the array while iterating `Array#each` checks the array's current length on every step rather than taking a snapshot. The rdoc notes that the array may be modified during iteration. Two consequences follow: - **Appending** inside the block means the new elements are visited too, so `a.each { |x| a << x }` never ends. - **Deleting** inside the block shifts later elements left, so the element after a deleted one is skipped. If you must change the collection, iterate over a copy (`a.dup.each`) or use a method designed for in-place removal. ## Implicit returns make it visible Ruby methods return the value of their last expression, so the receiver rule leaks into method results whether you intend it or not: ```ruby def print_results(rows) rows.each { |row| puts row } end ``` `print_results(finishers)` prints every row and then returns `finishers`. That has practical consequences: - A caller that writes `shown = print_results(finishers)` gets the live array, not a report, and can mutate it. - A test that asserts on the method's return value is really asserting on its argument. - If the method is meant to return nothing useful, ending it with an explicit `nil` makes that intent visible. The contrast with `puts` is a good memory aid: `puts` returns `nil`, `each` returns the collection, and `map` returns a new collection. Three iteration-adjacent calls, three different return values, and each one is regularly misremembered in interviews. ## What to say in an interview State the rule plainly: `each` returns its receiver and ignores the block's values. Then name the consequence: assigning the result gives back the original, aliased object, so use `map` when you want the block's results. Mentioning that `each_with_object` returns its memo and `cycle` returns `nil` shows you know the family, not just one method.
- What happens if the block pushes onto the same array that Array#each is walking?`Array#each` re-reads the array's length on every step, so elements appended during iteration are visited as well. `a.each { |x| a << x }` therefore never terminates. Deleting inside the block has the opposite effect: later elements shift left and one is skipped. Iterate over `a.dup` when the loop must change the array.
- Why is it useful that each returns the receiver rather than nil?It lets a method end with `rows.each { ... }` and still return the collection to its caller, and it lets you chain further calls, such as `rows.each { |r| audit(r) }.size`. The whole each family follows the same rule, except `each_with_object`, which returns its memo, and `cycle`, which returns `nil`.
saying these in an interview costs you the question
- each returns the values the block produced, just like map
- each returns nil because it is only meant for side effects
- each returns a fresh copy, so modifying the result is safe
- Hash#each returns an array of key-value pairs
- Deleting elements inside Array#each is safe because each works on a snapshot