5

I need to generate a unique six digit alpha-numeric code. To save in my database as voucher no: for every transaction.

Amal Kumar S
  • 15,555
  • 19
  • 56
  • 88
  • 2
    Why are you not simply auto-incrementing the code? They'd be numeric then. If you have too many codes (more than 6 digits), you can use a Base32 enoding of this number, e.g. – Jens May 06 '11 at 10:49
  • Possible duplicate? http://stackoverflow.com/questions/88311/how-best-to-generate-a-random-string-in-ruby – Teoulas May 06 '11 at 10:50
  • @Teoulas I don't think so. There's nothing in this question that states that the codes need to be (or appear) random. – Phrogz May 06 '11 at 17:26

6 Answers6

3

I used this

  require 'sha1'
  srand
  seed = "--#{rand(10000)}--#{Time.now}--"
  Digest::SHA1.hexdigest(seed)[0,6]

How to generate a random string in Ruby This link was useful

Community
  • 1
  • 1
Amal Kumar S
  • 15,555
  • 19
  • 56
  • 88
  • 1
    This is a very bad answer with high probability of collision. With 1 million iterations there are 767 **thousand** collisions on 215 thousand keys. Bumping the random number from `10000` to `10000000` reduces the collisions to "only" 31,000 out of 1,000,000, but any collisions is still bad. This is true whether Time.now is always the same or if it returns a new value each call (e.g. no more than one transaction per second). – Phrogz May 06 '11 at 17:25
  • In normal case this an acceptable one. Is there any other way for this. – rubyprince May 11 '11 at 10:20
0

A better way is to let the database handle the ids(incrementing). But if you insisting on generating them your self, you can use a random generator to generate an code, check it against db for uniqueness. then either accept or regenerate

Shaunak
  • 17,377
  • 5
  • 53
  • 84
  • This is a bad idea in theory as you start to fill up the available namespace, since your chance of collisions increases and in the extreme you may end up stuck in a loop of regenerating for a very long time before you find a free key. – Phrogz May 06 '11 at 16:57
0

I'd use the database to generate unique keys, but if you insist on doing it the hard way:

class AlnumKey

  def initialize
    @chars = ('0' .. '9').to_a + ('a' .. 'z').to_a
  end

  def to_int(key)
    i = 0
    key.each_char do |ch|
      i = i * @chars.length + @chars.index(ch)
    end
    i
  end

  def to_key(i)
    s = ""
    while i > 0 
      s += @chars[i % @chars.length]
      i /= @chars.length
    end
    s.reverse 
  end

  def next_key(last_key)
    to_key(to_int(last_key) + 1) 
  end
end

al = AlnumKey.new
puts al.next_key("ab")
puts al.next_key("1")
puts al.next_key("zz")

Of course, you'll have to store your current key somewhere, and this is in no way thread / multisession-safe etc.

Frank Schmitt
  • 30,195
  • 12
  • 73
  • 107
0

With the following restrictions:

  1. Valid only until 2038-12-24 00:40:35 UTC
  2. Generates no more than once within a second

you can use this simple code:

Time.now.to_i.to_s(36)
# => "lks3bn"
sawa
  • 165,429
  • 45
  • 277
  • 381
  • 2
    What will happen if the system time is changed? – Zabba May 06 '11 at 17:10
  • at a time I need to get different voucher ID's – Amal Kumar S May 06 '11 at 18:12
  • @Zabba That will cause a problem, but I assumed it will not be so frequent. In that case, the system will have to be down for maintenance for the time span that equals the difference between the new and the old time zones. It will be less than a day, at worst. If the new time zone goes ahead, the system does not have to be down. – sawa May 06 '11 at 22:22
0
class IDSequence
  attr_reader :current
  def initialize(start=0,digits=6,base=36)
    @id, @chars, @base = start, digits, base
  end
  def next
    s = (@id+=1).to_s(@base)
    @current = "0"*(@chars-s.length) << s
  end
end

id = IDSequence.new
1234.times{ id.next }

puts id.current
#=> 0000ya

puts id.next
#=> 0000yb

9876543.times{ id.next }
puts id.current
#=> 05vpqq
Phrogz
  • 296,393
  • 112
  • 651
  • 745
0

This would eleviate the time collision issue by getting the milli seconds

(Time.now.to_f*1000.0).to_i
Nath
  • 6,774
  • 2
  • 28
  • 22