659

I want to know if there is a much cleaner way of doing this. Basically, I want to pick a random element from an array of variable length. Normally, I would do it like this:

myArray = ["stuff", "widget", "ruby", "goodies", "java", "emerald", "etc" ] item = myArray[rand(myarray.length)] 

Is there something that is more readable / simpler to replace the second line? Or is that the best way to do it. I suppose you could do myArray.shuffle.first, but I only saw #shuffle a few minutes ago on SO, I haven't actually used it yet.

1
  • 13
    Good Answer below but a general point about shuffle. I would imagine suffling the full array would be much more intensive than just getting a random number so it wouldn't be a good direction to go. Commented Dec 19, 2011 at 18:50

7 Answers 7

1289

Just use Array#sample:

[:foo, :bar].sample # => :foo, or :bar :-) 

It is available in Ruby 1.9.1+. To be also able to use it with an earlier version of Ruby, you could require "backports/1.9.1/array/sample".

Note that in Ruby 1.8.7 it exists under the unfortunate name choice; it was renamed in later version so you shouldn't use that.

Although not useful in this case, sample accepts a number argument in case you want a number of distinct samples.

Sign up to request clarification or add additional context in comments.

5 Comments

I should have known that you would have a perfect answer for me (since most Ruby questions I browse here have your input somewhere). I am glad you pointed out the versioning; I am using 1.9.2. apidock (mladen's comment) does not have sample; neither does ruby-doc. In your opinion, what is the best reference for Ruby, updated to 1.9?
.sample is such an non obvious method name. I loved choice much better. Change it back Ruby developers!
Conveniently, you can also use sample on an ActiveRecord::Relation; e.g. @joe.pencils.sample will randomly choose a writing implement from Joe's pencil collection (assuming normal Rails stuff here).
@Purplejacket: Yes, but it might not be the best way to do this. ActiveRecord::Relation will delegate the call to an array, so it will load all the records and then pick one randomly.
@Marc-AndréLafortune: that is an excellent point!! A more optimized approach might be some inline SQL combined with this: stackoverflow.com/questions/580639/…
104

myArray.sample(x) can also help you to get x random elements from the array.

1 Comment

@Redithion It's also worth noting, that the difference between: my_array.sample(1) == [sample] and my_array.sample == sample to provide explicitly what you mean
25
myArray.sample 

will return 1 random value.

myArray.shuffle.first 

will also return 1 random value.

4 Comments

The shuffle is superfluous.
Technically this random method is correct too stackoverflow.com/a/84747/2047418
@superluminary, I think these were meant to both be individually acceptable answers, rather than sequential steps. Someone could either call the sample method on an array or they could shuffle the array and pick the first option. Depending on performance and use-case (whether you need multiple results, etc.), both are valid. The sample method is pretty simple and easy to rely on for most cases, but shuffle.first is more performant and has simpler source code (see random_user_0891's comparison response).
@boyd - you are correct. I retract.
17

Random Number of Random Items from an Array

def random_items(array) array.sample(1 + rand(array.count)) end 

Examples of possible results:

my_array = ["one", "two", "three"] my_array.sample(1 + rand(my_array.count)) => ["two", "three"] => ["one", "three", "two"] => ["two"] 

3 Comments

@fwilson random collections of objects in any order. Also good for testing different combinations or generating stub data
why not extend class Array ? [].total_random would be way cooler. comeon its ruby. its objective!
This would never return an empty array. You need to place 1 + in a different place.
8

Here are some benchmark tests I performed on some of the answers posted here, using sample was consistently faster than the rest.

test_arr = ["stuff", "widget", "ruby", "goodies", "java", "emerald" ] Benchmark.ips do |x| x.report("1 - sample") { test_arr.sample } x.report("2 - shuffle") { test_arr.shuffle.first } x.report("3 - length") { rand(test_arr.length) } x.report("4 - rand rand") { test_arr.sample(1 + rand(test_arr.count)) } x.report("5 - rand el") { test_arr[rand(test_arr.count)]} x.report("6 - switch") { case rand(0..test_arr.length) when 0 test_arr[0] when 1 test_arr[1] when 2 test_arr[2] when 3 test_arr[3] when 4 test_arr[4] when 5 test_arr[5] end } x.compare! 

The tests were run on a MacBook Pro (15-inch, 2018), 2.6 GHz 6-Core Intel Core i7, 32 GB 2400 MHz DDR4

Warming up -------------------------------------- 1 - sample 713.455k i/100ms 2 - shuffle 253.848k i/100ms 3 - length 489.078k i/100ms 4 - rand rand 236.396k i/100ms 5 - rand el 447.244k i/100ms 6 - switch 419.272k i/100ms Calculating ------------------------------------- 1 - sample 7.505M (± 3.2%) i/s - 37.813M in 5.044078s 2 - shuffle 2.661M (± 2.1%) i/s - 13.454M in 5.057659s 3 - length 5.021M (± 1.6%) i/s - 25.432M in 5.066159s 4 - rand rand 2.352M (± 2.4%) i/s - 11.820M in 5.029415s 5 - rand el 4.452M (± 2.2%) i/s - 22.362M in 5.025623s 6 - switch 4.324M (± 1.1%) i/s - 21.802M in 5.043294s Comparison: 1 - sample: 7504636.7 i/s 3 - length: 5021326.6 i/s - 1.49x (± 0.00) slower 5 - rand el: 4452078.6 i/s - 1.69x (± 0.00) slower 6 - switch: 4323511.6 i/s - 1.74x (± 0.00) slower 2 - shuffle: 2661267.7 i/s - 2.82x (± 0.00) slower 4 - rand rand: 2351630.7 i/s - 3.19x (± 0.00) slower 

Comments

1
arr = [1,9,5,2,4,9,5,8,7,9,0,8,2,7,5,8,0,2,9] arr[rand(arr.count)] 

This will return a random element from array.

If You will use the line mentioned below

arr[1+rand(arr.count)] 

then in some cases it will return 0 or nil value.

The line mentioned below

rand(number) 

always return the value from 0 to number-1.

If we use

1+rand(number) 

then it may return number and arr[number] contains no element.

Comments

-8
class String def black return "\e[30m#{self}\e[0m" end def red return "\e[31m#{self}\e[0m" end def light_green return "\e[32m#{self}\e[0m" end def purple return "\e[35m#{self}\e[0m" end def blue_dark return "\e[34m#{self}\e[0m" end def blue_light return "\e[36m#{self}\e[0m" end def white return "\e[37m#{self}\e[0m" end def randColor array_color = [ "\e[30m#{self}\e[0m", "\e[31m#{self}\e[0m", "\e[32m#{self}\e[0m", "\e[35m#{self}\e[0m", "\e[34m#{self}\e[0m", "\e[36m#{self}\e[0m", "\e[37m#{self}\e[0m" ] return array_color[rand(0..array_color.size)] end end puts "black".black puts "red".red puts "light_green".light_green puts "purple".purple puts "dark blue".blue_dark puts "light blue".blue_light puts "white".white puts "random color".randColor 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.