To check if a digit is inside an integer (ruby)
Chinese people don't like numbers with number 4. I am going to implement a membership program with member numbers not including number 4, for example:
number = 3
number.next.has4?
=> true
how can the method be made has4?
(efficiently)?
** EDIT
Thanks for the answers, I did some simple benchmarking as a reference:
class Fixnum
def has4a?
String(self).index('4') != nil
end
end
class Fixnum
def has4b?
self.to_s[/4/]
end
end
number = 3
puts Time.now
n = 0
while n < 1000000
number.next.has4a?
n += 1
end
puts Time.now
n = 0
while n < 1000000
number.next.has4b?
n += 1
end
puts Time.now
the result on my PC shows index
faster than regex
:
> ruby has4.rb
Tue May 11 18:36:04 +0800 2010
Tue May 11 18:36:05 +0800 2010
Tue May 11 18:36:11 +0800 2010
Edited below to include all 4 solutions and make it easier to see the duration of each one:
class Fixnum
def has4a?
String(self).index('4') != nil
end
end
class Fixnum
def has4b?
self.to_s[/4/]
end
end
class Fixnum
def has4c?
temp = self
while temp > 0
if (temp % 10) == 4
return true
end
temp /= 10
end
false
end
end
class Fixnum
def digits
d, m = divmod(10)
d > 0 ? d.digits + [m] : [m]
end
def has4d?
self.digits.member?(4)
end
end
before_A = Time.now
n = 0
has4 = 0
no4 = 0
while n < 5000000
has4 += 1 if n.has4a?
no4 += 1 if !n.has4a?
n += 1
end
after_A = Time.now
puts after_A, has4, no4
puts "A duration: " + (after_A - before_A).to_s
before_B = Time.now
n = 0
has4 = 0
no4 = 0
while n < 5000000
has4 += 1 if n.has4b?
no4 += 1 if !n.has4b?
n += 1
end
after_B = Time.now
puts after_B, has4, no4
puts "B duration: " + (after_B - before_B).to_s
before_C = Time.now
n = 0
has4 = 0
no4 = 0
while n < 5000000
has4 += 1 if n.has4c?
no4 += 1 if !n.has4c?
n += 1
end
after_C = Time.now
puts after_C, has4, no4
puts "C duration: " + (after_C - before_C).to_s
before_D = Time.now
n = 0
has4 = 0
no4 = 0
while n < 5000000
has4 += 1 if n.has4d?
no4 += 1 if !n.has4d?
n += 1
end
after_D = Time.now
puts after_D, has4, no4
puts "D duration: " + (after_D - before_D).to_s
result (ruby 1.8.7 (2009-06-12 patchlevel 174) [i486-linux] on Karmic). Feel free to post data from other machines.
Tue May 11 16:25:38 -0400 2010
2874236
2125764
A duration: 35.375095
Tue May 11 16:26:19 -0400 2010
2874236
2125764
B duration: 40.659878
Tue May 11 16:27:38 -0400 2010
2874236
2125764
C duration: 79.12419
Tue May 11 16:31:28 -0400 2010
2874236
2125764
D duration: 229.573483
sorry for my previous typo and thanks to Matthew Flashen for correcting it. here's my benchmark:
>ruby has4.rb
Wed May 12 09:14:25 +0800 2010
2874236
2125764
A duration: 18.186685
Wed May 12 09:15:06 +0800 2010
2874236
2125764
B duration: 40.388816
Wed May 12 09:15:38 +0800 2010
2874236
2125764
C duration: 32.639162
Wed May 12 09:18:08 +0800 2010
2874236
2125764
D duration: 150.024529
>ruby -v
ruby 1.8.7 (2010-01-10 patchlevel 249) [i386-mingw32]
a source to share
Here's another solution: use a simple counter for your internal IDs. Then when you want to show the user your id #, do it in base 9, replacing all 4s with 9 seconds.
user_visible_id = internal_id.to_s(9).gsub('4','9').to_i
Then, by processing their information, you can return their internal id just as easily:
internal_id = user_visible_id.to_s.gsub('9', '4').to_i(9)
This way, creating internal IDs is easy (you don't have to loop through and check them until you get one without 4). If you wanted, you could wrap the oneup counter in a module so that the rest of your application will use user_visible_id
, which will reduce confusion:
module IDGen
@counter = 0
def self.next
@counter += 1
@counter.to_s(9).gsub('4','9').to_i
end
def self.reset
@counter = 0
end
end
#...
User.new( IDGen.next )
a source to share