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]

      

+2


a source to share


6 answers


class Fixnum
  def has4?
    String(self).index('4') != nil
  end
end

      



+7


a source


If you want to do it mathematically without converting the number to a string, the following algorithm will work:



while num > 0
    if (num % 10) == 4
        return true
    num = num / 10
return false 

      

+6


a source


sort of

def has4?
  self.to_s[/4/]
end

      

?

+3


a source


If you don't like strings, but like recursion:

class Fixnum
  def digits
    d, m = divmod(10)
    d > 0 ? d.digits + [m] : [m]
  end
end

12093.digits
#=> [1, 2, 0, 9, 3]
1.digits
#=> [1]
115.digits.member?(4)
#=> false
145.digits.member?(4)
#=> true

      

:)

+1


a source


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 )

      

+1


a source


numbers.select{|number|!number.include?('4')}

      

0


a source







All Articles