In Ruby 4.0, what do Set#|, &, -, ^ and subset? return, and how do they differ from Array's |, & and - operators?
answer
- each returns a new Set
- ^ keeps elements in exactly one side
- subset? alias <=, needs a Set
- Array#- keeps the receiver's duplicates
- 4.0: core class, prints Set[1, 2]
basics
~20 sSet#| is union, & intersection, - difference and ^ symmetric difference, each returning a new Set, and subset? (alias <=) requires a Set argument. Array's |, & and - return Arrays in the receiver's order, and Array#- keeps duplicates.
solid answer
~50 sIn Ruby 4.0 `Set` is a core class, so no `require` is needed. `a | b` (also `+` and `union`) is the union, `a & b` (`intersection`) the common elements, `a - b` (`difference`) what is in `a` but not `b`, and `a ^ b` the elements in exactly one of them; each returns a new Set and accepts any Enumerable on the right. `subset?` (alias `<=`), `superset?` (`>=`) and the proper forms `<` and `>` require a Set and raise `ArgumentError` (`value must be a set`) for an Array, while `intersect?` and `disjoint?` accept any Enumerable. Array has `|`, `&` and `-` too: they return Arrays, keep the receiver's order, and `|` and `&` drop duplicates, but `-` keeps the receiver's remaining duplicates, so `[1, 1, 2, 3] - [3]` is `[1, 1, 2]`. Array defines no `^`.
code
ruby · 14 linesholidays = Set["2026-12-24", "2026-12-25"]
training = Set["2026-12-25", "2026-12-30"]
holidays | training # => Set["2026-12-24", "2026-12-25", "2026-12-30"]
holidays & training # => Set["2026-12-25"]
training - holidays # => Set["2026-12-30"]
(holidays ^ training).size # => 2
Set["2026-12-25"] <= holidays # => true
Set["2026-12-25"] <= ["2026-12-25"] # ArgumentError: value must be a set
holidays.intersect?(["2026-12-24"]) # => true, any Enumerable
[1, 1, 2, 3] - [3] # => [1, 1, 2]
[0, 1, 1] | [2, 2, 3] # => [0, 1, 2, 3]go deeper
Recall the four Set operators and their results, and that Set needs no require in Ruby 4.0.
Explain which methods accept any Enumerable and which demand a Set, and how Array's operators keep order and handle duplicates.
Pick Set or Array operators deliberately: order and duplicates survive only in Arrays, while repeated membership checks favour Set.
Decide where collections should be typed as Sets in the domain so uniqueness is enforced by the structure, not by later uniq calls.
## Set in Ruby 4.0 `Set` is an unordered collection with no duplicates, combining Array-like convenience with Hash-like lookup. In **Ruby 4.0** it is a **core class**, reimplemented in C: - No `require "set"` is needed; the call still works but returns `false`, since the feature is already loaded. - `Set[1, 2]` and `[1, 2].to_set` both build a set, and `Set#inspect` now prints `Set[1, 2]`. - Ruby 3.2 to 3.4 already made `Set` available without `require`, by autoloading the old library. A set compares elements with `eql?` and `hash`, the same rules as Hash keys. ## The four operators Each operator returns a **new** Set and leaves both operands untouched. The right-hand side may be any Enumerable, such as an Array or a Range. | Operator | Aliases | Result | `Set[1, 2] op Set[2, 3]` | |---|---|---|---| | `\|` | `+`, `union` | in either | `Set[1, 2, 3]` | | `&` | `intersection` | in both | `Set[2]` | | `-` | `difference` | in the receiver only | `Set[1]` | | `^` | none | in exactly one | 1 and 3 | `a ^ b` is documented as equivalent to `(a | b) - (a & b)`, the **symmetric difference**. In-place versions exist for building up a set: `merge` adds everything from an enumerable, `subtract` removes it, and `add?` returns `nil` instead of the set when the element was already present. ## Subset and superset checks - `subset?` (alias `<=`) and `superset?` (alias `>=`) test containment. - `proper_subset?` (`<`) and `proper_superset?` (`>`) also require the sets to differ. - `<=>` returns `-1`, `0` or `1`, or `nil` when each set has elements the other lacks. - These methods **require a Set argument**: `Set[1].subset?([1, 2])` raises `ArgumentError` with `value must be a set`. - `intersect?` and `disjoint?` are more lenient and accept any Enumerable: `Set[1, 2, 3].intersect?([3, 4])` is `true`. ## Array's operators are not set operators Array defines `|`, `&` and `-` as well (with the method names `union`, `intersection` and `difference`), and they also compare with `eql?` and `hash`. They differ from Set's in ways interviewers like to test: 1. **They return Arrays** in the receiver's order, so ordering survives. 2. **`|` and `&` remove duplicates**: `[0, 1, 1] | [2, 2, 3]` is `[0, 1, 2, 3]`. 3. **`-` keeps the receiver's duplicates** that are not removed: `[1, 1, 2, 3] - [3]` is `[1, 1, 2]`. It removes every occurrence of each listed value, not one per match. 4. **There is no `^` on Array**; convert to Sets for a symmetric difference. 5. **Array#intersect?** exists as a quick any-in-common check. ## Building sets - `Set.new(enum)`, `Set[*items]` and `Enumerable#to_set` all build a set from existing data. - In Ruby 4.0, passing arguments to `to_set`, such as a Set subclass to build, is **deprecated**; call the class's `new` instead. - `delete?` mirrors `add?`: it returns `nil` when the element was absent. - Converting a Range works for finite ranges, but `(1..).to_set` raises `RangeError`. ## A clinic example A clinic keeps dates on which bookings are blocked, as ISO strings: - `holidays = Set["2026-12-24", "2026-12-25"]` and `training = Set["2026-12-25", "2026-12-30"]`. - `holidays | training` is every blocked date; `holidays & training` the dates blocked for both reasons. - `training - holidays` lists training days that still need rescheduling notices. - `holidays ^ training` shows dates blocked for exactly one reason, handy when auditing two calendars. - `Set["2026-12-25"] <= holidays` is `true`, but passing an Array to `<=` raises `ArgumentError`.
- What does Set#add? return, and why is it useful?`add?` returns the set when the element was new and `nil` when it was already present, so `if seen.add?(date)` both records the date and tells you whether it is a first occurrence, in one hash lookup.
- Do you still need require "set" in Ruby 4.0?No. `Set` is a core class in 4.0, and `require "set"` returns `false` because the feature is already provided. Ruby 3.2 to 3.4 also worked without the require, by autoloading the library on first use of `Set` or `to_set`.
- What does Set[1, 2] <=> Set[1, 3] return?`nil`. `Set#<=>` returns `-1`, `0` or `1` only when one set is a subset of, equal to, or a superset of the other; when each has an element the other lacks, the sets are not ordered and the result is `nil`.
saying these in an interview costs you the question
- Set#- modifies the receiver in place
- Set#subset? accepts any Array and converts it
- [1, 1, 2, 3] - [3] returns [1, 2] because - deduplicates
- Array#| keeps duplicates from both arrays
- Ruby 4.0 still needs require "set" before using Set