skip to content

In Ruby 4.0, what do Set#|, &, -, ^ and subset? return, and how do they differ from Array's |, & and - operators?

level: middleimportance: must knowfreq 55%

answer

  1. each returns a new Set
  2. ^ keeps elements in exactly one side
  3. subset? alias <=, needs a Set
  4. Array#- keeps the receiver's duplicates
  5. 4.0: core class, prints Set[1, 2]

basics

~20 s

Set#| 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 s

In 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 lines
ruby
holidays = 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

for a junior

Recall the four Set operators and their results, and that Set needs no require in Ruby 4.0.

for a middle

Explain which methods accept any Enumerable and which demand a Set, and how Array's operators keep order and handle duplicates.

for a senior

Pick Set or Array operators deliberately: order and duplicates survive only in Arrays, while repeated membership checks favour Set.

for a principal

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