In Ruby, what do any?, all?, none? and one? return on an empty collection, and what changes when you pass them a pattern?
answer
- vacuous truth on empty
- [].all? is true
- no block: element truthiness
- pattern === element
- include? uses ==, not ===
basics
~20 sOn 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.
solid answer
~40 sThe four predicates return `true` or `false`. On an empty collection `all?` and `none?` are `true` and `any?` and `one?` are `false`, which is why `invoices.all?(&:paid?)` reports "all paid" when there are no invoices. Without a block or argument they test the elements' own truthiness, so `[nil, false].any?` is `false`. With a **pattern** argument they test `pattern === element`: `days.any?(31..)`, `rows.all?(Invoice)`, `refs.none?(/DRAFT/)`. A block given together with a pattern is ignored and Ruby warns "given block not used". `any?`, `all?` and `none?` stop at the first element that decides the answer; `one?` stops at the second match. Unlike the patterns, `include?` and `count(obj)` compare with `==`.
code
ruby · 17 linesdays_late = [0, 12, 45]
days_late.any?(31..) # => true (Range#===)
days_late.all?(Integer) # => true (Module#===)
days_late.none? { |d| d > 90 } # => true
days_late.one?(0) # => true (exactly one element == 0)
[].all? { |inv| inv.paid? } # => true, block never runs
[].any? # => false
[nil, false].any? # => false
[30, 45.5].include?(Integer) # => false (== comparison)
[30, 45.5].any?(Integer) # => true (=== comparison)
days_late.any?(31..) { |d| d > 5 }
# warning: given block not used
# => truego deeper
Recall what each predicate asks and that [].all? is true while [].any? is false.
Explain the three forms, that a pattern argument uses ===, the ignored-block warning, and why include? and count(obj) use == instead.
Catch vacuous-truth bugs where an empty list passes an all? check, and prefer predicates that exit early over select followed by any?.
Push for explicit handling of empty inputs in business rules, since an all? over nothing silently becomes an approval.
## Four yes-or-no questions Ruby's `Enumerable` module, which `Array` also overrides natively, has four **predicate** methods. Each returns `true` or `false` and nothing else: | Method | Question it answers | Empty collection | |---|---|---| | `any?` | does at least one element match? | `false` | | `all?` | does every element match? | `true` | | `none?` | does no element match? | `true` | | `one?` | does exactly one element match? | `false` | The rdoc in Ruby's `enum.c` states the empty cases explicitly: for an empty receiver `all?` returns `true` and `any?` returns `false`, and the argument or block is not used. ## Why `[].all?` is true This is **vacuous truth**: there is no element that fails the test, so "every element passes" holds. It is logically sound and practically dangerous: 1. An accounts-receivable job checks `customer.invoices.all?(&:paid?)` before closing an account. 2. A customer whose invoices failed to load has an empty list. 3. The check passes, and the account is closed as settled. The fix is to state the real requirement: `invoices.any? && invoices.all?(&:paid?)`, or to treat an empty list as its own case before asking. ## Three ways to state the test Each predicate accepts one of three forms: - **No block, no argument**: tests each element's own truthiness. `[nil, 45].any?` is `true`; `[nil, false].any?` is `false`; `[nil, 45, nil].one?` is `true`. - **A block**: tests the block's return value for each element, e.g. `invoices.any? { |i| i.days_late > 30 }`. - **A pattern argument**: tests `pattern === element`. Because `===` is defined by the pattern's class, this covers several cases in one form: - a **class**: `rows.all?(Invoice)` checks types; - a **range**: `days.any?(31..)` checks bounds; - a **regexp**: `refs.none?(/DRAFT/)` checks text; - a plain **value**: `statuses.one?(:disputed)` checks equality. If you pass both a pattern and a block, the pattern wins, the block is ignored, and Ruby emits the warning `given block not used`. ## Early exit The predicates stop as soon as the answer is known: - `any?` stops at the first match; `all?` at the first failure; `none?` at the first match. - `one?` has to keep going after the first match, because a second one changes the answer; it stops at the second. That matters when the block is expensive or has side effects, and it is the main reason to write `invoices.any? { ... }` rather than `invoices.select { ... }.any?`, which evaluates the block for every element first. ## `include?` and `count` compare differently Two neighbouring methods look like predicates but use **`==`**, not `===`: - `include?(obj)`, also called `member?`, returns whether any element `== obj`. - `count(obj)` returns how many elements `== obj`; `count` with a block counts truthy block results, and plain `count` counts everything. So `[30, 45.5].include?(Integer)` is `false`, because no element equals the class `Integer`, while `[30, 45.5].any?(Integer)` is `true`. Mixing the two up is a common interview slip. ## Reading predicate code The four methods are related, and code often mixes them in ways that are easy to misread: - `none? { ... }` is the same as `!any? { ... }`, and reads better than the negation. - `all? { |i| !i.paid? }` is the same as `none?(&:paid?)`; the second form states the intent directly. - `any?` with no block is sometimes used as "not empty", but it means "has a truthy element": `[nil].any?` is `false` although the array has one element. `empty?` is the size question. Preferring the positive form and the pattern argument where they fit keeps business rules readable, which matters when those rules decide who gets a reminder letter. ## `one?` versus "exactly one element" `one?` counts **matches**, not elements. `[nil, 45, nil].one?` is `true` because exactly one element is truthy, and `[45, 60].one?` is `false` because two are. To ask whether a collection has a single element, compare its size instead. ## What a strong answer covers Give the empty-collection table first, including why `all?` is `true`. Then name the three forms, the `===` semantics of the pattern argument, the ignored block warning, and early exit. Finish by contrasting `include?` and `count(obj)`, which use `==`.
- How would you guard an all? check against an empty list?Make the non-empty requirement explicit: `invoices.any? && invoices.all?(&:paid?)`, or handle `invoices.empty?` first with its own outcome. `all?` on an empty collection is `true` by definition, so the check alone cannot tell "all paid" from "nothing loaded".
- Why does [30, 45.5].include?(Integer) return false while any?(Integer) returns true?`include?` asks whether any element `== Integer`, and neither 30 nor 45.5 equals the class object. `any?(Integer)` asks whether `Integer === element` for any element, and `Module#===` checks class membership, which 30 satisfies. `count(obj)` uses `==` too.
- When does one? stop iterating?At the second match. After one match it cannot yet answer, because another match would make the result `false`; once a second match appears it returns `false` immediately. If it reaches the end with exactly one match, it returns `true`.
saying these in an interview costs you the question
- all? on an empty collection returns false because nothing was checked
- any? with a pattern compares elements with ==
- Passing both a pattern and a block combines the two tests
- one? returns true when the collection has exactly one element
- include?(Integer) checks whether any element is an Integer