How do I pick randomly from an array?
Asked Answered
J

7

648

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.

Jolie answered 14/8, 2010 at 5:26 Comment(1)
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.Renovate
A
1273

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.

Alteration answered 14/8, 2010 at 14:39 Comment(5)
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?Jolie
.sample is such an non obvious method name. I loved choice much better. Change it back Ruby developers!Rohr
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).Urolith
@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.Donavon
@Marc-AndréLafortune: that is an excellent point!! A more optimized approach might be some inline SQL combined with this: #581139Urolith
T
104

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

Turves answered 4/7, 2013 at 12:11 Comment(1)
@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 meanAngadreme
S
25
myArray.sample

will return 1 random value.

myArray.shuffle.first

will also return 1 random value.

Semiautomatic answered 22/10, 2013 at 4:31 Comment(2)
The shuffle is superfluous.Pizzeria
Technically this random method is correct too https://mcmap.net/q/18290/-what-39-s-your-favorite-quot-programmer-quot-cartoonTenderhearted
C
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"]
Calley answered 7/10, 2013 at 8:31 Comment(3)
@fwilson random collections of objects in any order. Also good for testing different combinations or generating stub dataCalley
why not extend class Array ? [].total_random would be way cooler. comeon its ruby. its objective!Cosignatory
This would never return an empty array. You need to place 1 + in a different place.Refutative
D
6

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
Departmentalize answered 6/7, 2022 at 20:24 Comment(0)
A
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.

Aeschines answered 29/6, 2018 at 9:57 Comment(0)
A
-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
Anasarca answered 16/7, 2019 at 18:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.