1

Currently trying to generate a random number in a specific range; and ensure that it would be unique against others stored records.

Using Mysql. Could be like an id, incremented; but can't be it.

Currently testing other existing records in an 'expensive' manner; but I'm pretty sure that there would be a clean 1/2 lines of code to use

Currently using :

test = 0
Order.all.each do |ord|
  test = (0..899999).to_a.sample.to_s.rjust(6, '0')
  if Order.find_by_number(test).nil? then
    break
  end
end
return test

Thanks for any help

Ben
  • 5,030
  • 6
  • 53
  • 94
  • Please post the code you have written so far. – toro2k Feb 23 '14 at 11:37
  • @toro2k edited with an example. Let's say I'm not sure about how to make a quick and clean test and recall the random 'generator' if already existing – Ben Feb 23 '14 at 11:44
  • 1
    @Ben, I have post my answer. It takes performance into account and since you are going to use it quit often I guess, you should try it. – Rafa Paez Feb 23 '14 at 13:40
  • yep; clean one and quite understandable – Ben Feb 23 '14 at 14:07

3 Answers3

2

Here your are my one-line solution. It is also the quicker one since calls .pluck to retrieve the numbers from the Order table. .select instantiates an "Order" object for every record (that is very costly and unnecessary) while .pluck does not. It also avoids to iterate again each object with a .map to get the "number" field. We can avoid the second .map as well if we convert, using CAST in this case, to a numeric value from the database.

(Array(0...899999) - Order.pluck("CAST('number' AS UNSIGNED)")).sample.to_s.rjust(6, '0')
Rafa Paez
  • 4,820
  • 18
  • 35
  • what is the CAST UNSIGNED used for ? (I'll go to the docs mysql to check but you seem to be very well in the know) – Ben Feb 23 '14 at 14:11
  • It is the safest way to convert a varchar into a kind of integer value in MySQL (since "order.number" is in varchar). Check this out http://stackoverflow.com/questions/12126991/cast-from-varchar-to-int-mysql – Rafa Paez Feb 23 '14 at 14:17
  • You mean the safest used in this function' scope or globally ? My 'number' field is an integer... but you'll say that an integer field can't be zerofilled (000018 for example) ? Got to check on my side – Ben Feb 23 '14 at 16:11
  • I(we) though that your 'number' field was a `varchar` because in your example you are converting it to `string`: `to_s.rjust(6, '0')`. If it is an integer you can remove the `CAST`. – Rafa Paez Feb 23 '14 at 16:23
  • Yes sure, I have to assume not being an expert on this one and not being that accurate. Thanks a lot for your very clean answer, helped me a lot – Ben Feb 23 '14 at 17:08
1

I think, you can do something like below :

def uniq_num_add(arr)
 loop do
   rndm = rand(1..15)  # I took this range as an example
    # random number will be added to the array, when the number will 
    # not be present
    break arr<< "%02d" % rndm unless arr.include?(rndm)
  end  
end  

array = []

3.times do
 uniq_num_add(array)
end  

array # => ["02", "15", "04"]
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
1

I would do something like this:

# gets all existing IDs
existing_ids = Order.all.select(:number).map(&:number).map(&:to_i)

# removes them from the acceptable range
available_numbers = (0..899999).to_a - existing_ids

# choose one (which is not in the DB)
available_numbers.sample.to_s.rjust(6, '0')
Uri Agassi
  • 36,848
  • 14
  • 76
  • 93
  • available_numberS (can't edit this just one letter thing). but yeah, thx ! – Ben Feb 23 '14 at 11:52
  • 1
    @Uri, you solution works and is fine but this code is quite inefficient `Order.all.select(:number).map(&:number).map(&:to_i)` for serveral reasons. – Rafa Paez Feb 23 '14 at 13:43