Array#sample

Updated April 1, 2026 · Array Methods
ruby array sample random stdlib

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)

CallReturns
array.sampleA 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:

MethodReturnsSize of result
sampleRandom subset of elements1 (default) or n you specify
shuffleAll elements, reorderedAlways 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