How to generate a random string in Ruby

Viewed 487878

I'm currently generating an 8-character pseudo-random uppercase string for "A" .. "Z":

value = ""; 8.times{value  << (65 + rand(25)).chr}

but it doesn't look clean, and it can't be passed as an argument since it isn't a single statement. To get a mixed-case string "a" .. "z" plus "A" .. "Z", I changed it to:

value = ""; 8.times{value << ((rand(2)==1?65:97) + rand(25)).chr}

but it looks like trash.

Does anyone have a better method?

51 Answers
(0...8).map { (65 + rand(26)).chr }.join

I spend too much time golfing.

(0...50).map { ('a'..'z').to_a[rand(26)] }.join

And a last one that's even more confusing, but more flexible and wastes fewer cycles:

o = [('a'..'z'), ('A'..'Z')].map(&:to_a).flatten
string = (0...50).map { o[rand(o.length)] }.join

If you want to generate some random text then use the following:

50.times.map { (0...(rand(10))).map { ('a'..'z').to_a[rand(26)] }.join }.join(" ")

this code generates 50 random word string with words length less than 10 characters and then join with space

This solution generates a string of easily readable characters for activation codes; I didn't want people confusing 8 with B, 1 with I, 0 with O, L with 1, etc.

# Generates a random string from a set of easily readable characters
def generate_activation_code(size = 6)
  charset = %w{ 2 3 4 6 7 9 A C D E F G H J K M N P Q R T V W X Y Z}
  (0...size).map{ charset.to_a[rand(charset.size)] }.join
end

Since Ruby 2.5, it's really easy with SecureRandom.alphanumeric:

len = 8
SecureRandom.alphanumeric(len)
=> "larHSsgL"

It generates random strings containing A-Z, a-z and 0-9 and therefore should be applicable in most use-cases. And they are generated randomly secure, which might be a benefit, too.


This is a benchmark to compare it with the solution having the most upvotes:

require 'benchmark'
require 'securerandom'

len = 10
n = 100_000

Benchmark.bm(12) do |x|
  x.report('SecureRandom') { n.times { SecureRandom.alphanumeric(len) } }
  x.report('rand') do
    o = [('a'..'z'), ('A'..'Z'), (0..9)].map(&:to_a).flatten
    n.times { (0...len).map { o[rand(o.length)] }.join }
  end
end

                   user     system      total        real
SecureRandom   0.429442   0.002746   0.432188 (  0.432705)
rand           0.306650   0.000716   0.307366 (  0.307745)

So the rand solution only takes about 3/4 of the time of SecureRandom. That might matter if you generate a lot of strings, but if you just create some random string from time to time I'd always go with the more secure implementation since it is also easier to call and more explicit.

I can't remember where I found this, but it seems like the best and the least process intensive to me:

def random_string(length=10)
  chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789'
  password = ''
  length.times { password << chars[rand(chars.size)] }
  password
end
require 'sha1'
srand
seed = "--#{rand(10000)}--#{Time.now}--"
Digest::SHA1.hexdigest(seed)[0,8]

Another method I like to use:

 rand(2**256).to_s(36)[0..7]

Add ljust if you are really paranoid about the correct string length:

 rand(2**256).to_s(36).ljust(8,'a')[0..7]

My favorite is (:A..:Z).to_a.shuffle[0,8].join. Note that shuffle requires Ruby > 1.9.

We've been using this on our code:

class String

  def self.random(length=10)
    ('a'..'z').sort_by {rand}[0,length].join
  end

end

The maximum length supported is 25 (we're only using it with the default anyway, so hasn't been a problem).

Someone mentioned that 'a'..'z' is suboptimal if you want to completely avoid generating offensive words. One of the ideas we had was removing vowels, but you still end up with WTFBBQ etc.

With this method you can pass in an abitrary length. It's set as a default as 6.

def generate_random_string(length=6)
  string = ""
  chars = ("A".."Z").to_a
  length.times do
    string << chars[rand(chars.length-1)]
  end
  string
end

I like Radar's answer best, so far, I think. I'd tweak a bit like this:

CHARS = ('a'..'z').to_a + ('A'..'Z').to_a
def rand_string(length=8)
  s=''
  length.times{ s << CHARS[rand(CHARS.length)] }
  s
end

Here is a improve of @Travis R answer:

 def random_string(length=5)
    chars = 'abdefghjkmnpqrstuvwxyzABDEFGHJKLMNPQRSTUVWXYZ'
    numbers = '0123456789'
    random_s = ''
    (length/2).times { random_s << numbers[rand(numbers.size)] }
    (length - random_s.length).times { random_s << chars[rand(chars.size)] }
    random_s.split('').shuffle.join
  end

At @Travis R answer chars and numbers were together, so sometimes random_string could return only numbers or only characters. With this improve at least half of random_string will be characters and the rest are numbers. Just in case if you need a random string with numbers and characters

To make your first into one statement:

(0...8).collect { |n| value  << (65 + rand(25)).chr }.join()

Use 'SafeRandom' Gem GithubLink

It will provide the easiest way to generate random values for Rails2, Rails 3, Rails 4, Rails 5 compatible.

The following worked well for me

def generate_random_password(min_length, max_length)
    length = SecureRandom.random_number(max_length - min_length) + min_length
    character_sets = [ 
      ('a'..'z').to_a,
      ('A'..'Z').to_a,
      ('0'..'9').to_a,
      "~!@^&*()_-+=[]|:;<,>.?".split('')
    ]   
    retval = []
    #   
    # Add one character from each set
    #   
    character_sets.each do |character_set|
      character = character_set[SecureRandom.random_number(character_set.count)]
      retval.push character
    end 
    #   
    # Fill the rest of the password with a random character from a random set
    #   
    i = character_sets.count - 1 
    while i < length
      character_set = character_sets[SecureRandom.random_number(character_sets.count)]
      character = character_set[SecureRandom.random_number(character_set.count)]
      retval.push character
      i += 1
    end
    retval.shuffle.join
  end

This is almost as ugly but perhaps as step in right direction?

 (1..8).map{|i| ('a'..'z').to_a[rand(26)]}.join

In ruby 1.9 one can use Array's choice method which returns random element from array

I don't know ruby, so I can't give you the exact syntax, but I would set a constant string with the list of acceptable characters, then use the substring operator to pick a random character out of it.

The advantage here is that if the string is supposed to be user-enterable, then you can exclude easily confused characters like l and 1 and i, 0 and O, 5 and S, etc.

Related