Ruby Tuesday – Array#sample

Today’s Ruby Tuesday is on Array#sample.

Array#sample has a couple of different forms it can take.

The first form it can take is a version where no arguments are passed; in this case it returns a random element from the array it was called on, or nil if the array is empty.

[1, 2, 3, 4, 5].sample
# => 4
[1, 2, 3, 4, 5].sample
# => 1
[1, 2, 3, 4, 5].sample
# => 2
[].sample
# => nil
[:heart, :diamond, :club, :spade].sample
# => :club

Array#sample can also take as its argument a count of the number of random items to be returned.

[1, 2, 3, 4, 5].sample 2
# => [1, 2]
[1, 2, 3, 4, 5].sample 2
# => [2, 1]
[1, 2, 3, 4, 5].sample 2
# => [1, 3]
[1, 2, 3, 4, 5].sample 2
# => [3, 5]
[1, 2, 3, 4, 5].sample 2
# => [5, 2]

If the argument to the count is greater than, or equal to, the number of items in the array, then the array is returned.

[:heart, :diamond, :club, :spade].sample 6
# => [:heart, :diamond, :spade, :club]

This has two subtle but important consequences. First if we call sample with a count on an empty array, an empty array is returned, instead of a nil like we would get back if we just call Array#sample with no count argument. This includes calling Array#sample with a count of zero on an empty array.

[].sample
# => nil
[].sample 0
# => []
[].sample 1
# => []
[].sample 3
# => []

Second, it shows that as we sample the array, we are not going to get duplicate elements, unless they were in the array to being with.

On both of these forms, a random number generator can also be passed. This can give us a repeatable generation sequence as we can prime the generator with a given seed.

generatorA = Random.new 1
# => #<Random:0x007f86c3925878>
(1..100).to_a.sample(random: generatorA)
# => 38
(1..100).to_a.sample(random: generatorA)
# => 13
(1..100).to_a.sample(random: generatorA)
# => 73
generatorB = Random.new 1
# => #<Random:0x007f86c497dd10>
(1..100).to_a.sample(random: generatorB)
# => 38
(1..100).to_a.sample(random: generatorB)
# => 13
(1..100).to_a.sample(random: generatorB)
# => 73
(1..100).to_a.sample(random: generatorB)
# => 10
(1..100).to_a.sample(random: generatorA)
# => 10

(1..100).to_a.sample(4, random: generatorA)
# => [76, 6, 82, 66]
(1..100).to_a.sample(4, random: generatorA)
# => [17, 2, 79, 74]
(1..100).to_a.sample(4, random: generatorB)
# => [76, 6, 82, 66]
(1..100).to_a.sample(4, random: generatorB)
# => [17, 2, 79, 74]

And if we want to pick a random card from a deck of cards, or even draw a 5 card hand we can use Array#sample on an array of “playing cards”.

suit = [:heart, :diamond, :club, :spade]
# => [:heart, :diamond, :club, :spade]
rank = [:ace, 2, 3, 4, 5, 6, 7, 8, 9, :jack, :queen, :king]
# => [:ace, 2, 3, 4, 5, 6, 7, 8, 9, :jack, :queen, :king]
suit.product(rank).sample 1
# => [[:heart, :jack]]
suit.product(rank).sample 5
# => [[:club, 2], [:diamond, 2], [:spade, 7], [:club, 8], [:spade, :king]]

–Proctor