rubyguides

Array#shuffle

Array#shuffle(random: Random) → Array

Array#shuffle

Returns the receiver’s elements in a random order, leaving the original untouched. Use it when you need a random permutation: drawing a hand of cards, randomizing a playlist, picking a fair test ordering, choosing which worker gets the next job. If you want the in-place version, that is shuffle! (see the comparison below).

Signature and return value

Array#shuffle(random: Random) → Array
  • Returns: a new Array. The receiver is never modified.
  • Argument: optional keyword random:. Any object that responds to rand(integer) works in practice, though the docs only guarantee Random instances.
  • Empty source: [].shuffle returns [].
  • Single element: [42].shuffle returns [42].
  • Available since at least Ruby 1.8.7.

Basic Usage

The non-destructive behavior is the detail readers miss most often. shuffle does not replace elements in place; it hands you a brand new instance.

cards = %w[ace king queen jack ten]
hand  = cards.shuffle
# => e.g. ["queen", "ten", "ace", "king", "jack"]   # order varies per run
cards
# => ["ace", "king", "queen", "jack", "ten"]        # original is intact

If you assign the result back to the same variable, you may have looked at the source one too many times and actually wanted shuffle!.

Reproducible Shuffles with random:

Pass random: Random.new(seed) to make the order deterministic. The same seed produces the same permutation, which is exactly what you want in tests, fuzz fixtures, and reproducible demos.

a = [1, 2, 3, 4, 5]
a.shuffle(random: Random.new(1))
# => [4, 2, 5, 1, 3]
a.shuffle(random: Random.new(1))
# => [4, 2, 5, 1, 3]   # repeatable: same seed, same order

The random: argument is keyword-only on Ruby 2.0 and later. Calling arr.shuffle(some_rng) positionally raises ArgumentError on modern Ruby, so prefer the keyword form even when the value is short.

Shuffle vs shuffle!

shuffle! mutates the receiver and returns it. One annoying difference shows up on an empty source: shuffle! returns nil, while shuffle returns []. If you ever chain off the bang version, an empty source will blow up with NoMethodError on nil.

arr = [1, 2, 3]
arr.shuffle!.equal?(arr)   # => true   (bang returns self)

[].shuffle!                 # => nil    (empty case for the bang)
[].shuffle                  # => []     # non-bang on empty is always []

A simple guard fixes the chaining hazard: arr.shuffle! || arr keeps the empty case safe.

Common Gotchas

  • The default PRNG is shared. Calling Kernel#srand(n) in one test affects every unshuffled arr.shuffle later in the same process, because both use Random::DEFAULT. Use random: Random.new(...) to isolate.
  • Shuffle is a shallow copy. If your elements are mutable (strings, hashes, structs), editing one in the shuffled result also mutates the element in the original.
  • shuffle is a permutation, not a sort. It does not respect any comparator and is not stable. For “sort then randomize” or “random within buckets”, you have to compose it yourself.
  • Frozen sources are fine. [:a, :b, :c].freeze.shuffle returns a fresh copy and does not raise. This is different from sort!, which refuses to mutate a frozen receiver.
  • Duck-typed RNGs work, but pass an actual Random instance when you can. Custom objects are a debugging tax if anyone wonders where the order came from.

See Also

  • Array#sample — pick one or more random elements without permuting the whole thing.
  • Array#sort — deterministic ordering, the non-random counterpart to shuffle.
  • Ruby Array Methods Guide — a broader tour of the Array API worth bookmarking.