Array#sample
What does sample do?
Array#sample returns one or more random elements from an array. Without an argument, it returns a single random element. With an argument n, it returns an array of n unique random elements.
The method does not modify the original array — it returns a selection while leaving the caller intact.
This is useful for picking random items from a collection: a winner from a contest, a random record from a dataset, a sample of test data, or a feature flag variant.
Basic Usage
Returning a single random element
fruits = ["apple", "banana", "cherry", "date"]
fruits.sample # => "cherry" (random each time)
fruits.sample # => "apple"
fruits.sample # => "date"
Calling sample on an empty array returns nil:
[].sample # => nil
Returning multiple random elements
Pass an integer n to get n random elements. The elements are unique — no element appears twice in the result:
fruits = ["apple", "banana", "cherry", "date", "elderberry"]
fruits.sample(2) # => ["banana", "elderberry"]
fruits.sample(3) # => ["apple", "cherry", "date"]
If n is 0, an empty array is returned:
fruits.sample(0) # => []
If n is greater than the number of elements in the array, all elements are returned in random order — no error is raised:
fruits = ["apple", "banana"]
fruits.sample(5) # => ["banana", "apple"] (all elements, shuffled)
How many to pick: sample vs sample(n)
| Call | Returns |
|---|---|
array.sample | A single random element (or nil) |
array.sample(n) | An array of up to n unique random elements |
deck = [:ace, :king, :queen, :jack, :ten]
deck.sample # => :queen
deck.sample(3) # => [:king, :ace, :ten]
Sampling Without Replacement
By default, sample selects without replacement — once an element is chosen, it cannot be chosen again in the same call. This is the correct behaviour for most use cases like selecting winners or shuffling a hand of cards.
colors = ["red", "blue", "green", "yellow"]
colors.sample(3) # => ["green", "red", "yellow"] (3 unique choices)
To sample with replacement (allowing the same element to appear multiple times), you must call sample repeatedly or use a different approach:
# With replacement: roll a die 10 times
die = [1, 2, 3, 4, 5, 6]
rolls = 10.times.map { die.sample }
rolls # => [3, 1, 4, 4, 2, 6, 3, 5, 1, 2]
Custom Random Number Generator
Pass a random: keyword argument with a Random object to control the source of randomness. This is useful for reproducible test data or simulations:
rng = Random.new(42)
[1, 2, 3, 4, 5].sample(3, random: rng) # => [5, 1, 3]
[1, 2, 3, 4, 5].sample(3, random: rng) # => [5, 1, 3] (same result)
[1, 2, 3, 4, 5].sample(3) # => [2, 4, 1] (different)
The random: keyword also accepts any object that responds to #rand, such as Random::DEFAULT:
# Deterministic: same seed always gives same sequence
r = Random.new(123)
[10, 20, 30, 40, 50].sample(2, random: r) # => [20, 10]
sample vs shuffle
Both sample and shuffle return random elements from an array, but they differ in what they return:
| Method | Returns | Size of result |
|---|---|---|
sample | Random subset of elements | 1 (default) or n you specify |
shuffle | All elements, reordered | Always the full array |
deck = [:ace, :king, :queen, :jack, :ten]
deck.shuffle # => [:queen, :ten, :ace, :jack, :king] (all 5, shuffled)
deck.sample # => :king (1 random)
deck.sample(2) # => [:ace, :queen] (2 random)
Think of it this way: shuffle is for reordering a whole collection, while sample is for picking a random subset.
sample vs rand with index
You can generate a random index with rand and use it to access an array element, but this is more error-prone than sample:
names = ["Alice", "Bob", "Carol"]
# Manual random index — awkward and easy to get wrong
names[rand(names.length)] # => "Carol"
# Equivalent with sample — cleaner
names.sample # => "Bob"
Using rand(array.length) breaks down for picking multiple elements because you have to track which indices you’ve already chosen. sample(n) handles this correctly and efficiently.
Practical Examples
Picking a random winner
contestants = ["Alice", "Bob", "Carol", "Dave", "Eve"]
winner = contestants.sample
puts "The winner is #{winner}!"
# => The winner is Carol!
Selecting multiple winners
contestants = ["Alice", "Bob", "Carol", "Dave", "Eve", "Frank"]
first = contestants.sample(2)
puts "Winners: #{first.join(' and ')}"
# => Winners: Dave and Carol
Random password from a word list
words = ["apple", "banana", "cherry", "dragon", "elephant", "forest", "galaxy", "harbor"]
password = 4.times.map { words.sample }.join("-")
password # => "forest-banana-galaxy-dragon"
Random feature flags
features = {
new_checkout: 0.1, # 10% of users
dark_mode: 0.25, # 25% of users
beta_search: 0.05 # 5% of users
}
enabled = features.select { |_, ratio| rand <= ratio }.keys
enabled # => [:dark_mode] (random each call)
Sampling test data
users = (1..1000).map { |id| { id: id, name: "User #{id}" } }
# Get 5 random test users
test_sample = users.sample(5)
test_sample.each { |u| puts "Testing user #{u[:id]}" }
# => Testing user 342
# => Testing user 7
# => Testing user 891
# => Testing user 56
# => Testing user 623
Randomising a hand of cards
deck = [:ace, :king, :queen, :jack, :ten].product([:hearts, :diamonds, :clubs, :spades]).flatten(1)
hand = deck.sample(5)
hand # => [[:queen, :clubs], [:ace, :hearts], [:ten, :diamonds], [:jack, :spades], [:king, :clubs]]
Performance Considerations
sample is implemented efficiently. For a single-element sample, Ruby uses a constant-time random index lookup — O(1). For multi-element sampling, Ruby uses a variant of reservoir sampling that avoids fully shuffling the array, making it more efficient than shuffle when you only need a subset.
Performance degrades gracefully as n approaches the array size. If you need all elements randomised, shuffle is equivalent and may be clearer in intent.
require "benchmark"
array = (1..10_000).to_a
Benchmark.measure { array.sample }.real # => ~0.00001s (single element)
Benchmark.measure { array.sample(100) }.real # => ~0.0001s
Benchmark.measure { array.sample(5000) }.real # => ~0.002s
Benchmark.measure { array.shuffle }.real # => ~0.003s
For very large arrays where you only need a small subset, sample(n) is typically faster than shuffle.first(n).
Edge Cases
Empty array
[].sample # => nil
[].sample(0) # => []
[].sample(1) # => [] (not nil — n is clamped to array length)
n is 0
[1, 2, 3].sample(0) # => []
n exceeds array length
[1, 2].sample(5) # => [2, 1] (all elements, in random order)
Single-element array
[42].sample # => 42
[42].sample(1) # => [42]
[42].sample(2) # => [42]
nil in the array
[1, nil, 3, nil, 5].sample # => nil (nil is a valid element)
[1, nil, 3, nil, 5].sample(2) # => [nil, 1] (nil can be selected)
See Also
- /reference/array-methods/array-reject/ — Returns a new array with elements for which the block evaluates to false
- /reference/array-methods/array-select/ — Returns elements for which the block evaluates to true
- /reference/enumerable/enumerable-include/ — Checks whether an element is present in the array