Randomtheories
Randomtheories

Reputation: 1290

Better way to check for multiple regex conditions in Ruby?

I'm looking for a better way to check, if a string matches multiple regex patterns (or not). This is my approach so far:

class Bob
  def hey(remark)
    if remark =~ upcase && !(remark =~ downcase) && !(remark =~ numbers)
      'Whoa, chill out!'
    elsif remark =~ ends_with_questionmark
      'Sure.'
    elsif (remark =~ numbers) && (remark =~ upcase) && !(remark =~ downcase)
      'Whoa, chill out!'
    else
      'Whatever.'
    end 
  end
  def numbers
    /[\d]+/
  end
  def downcase
    /[a-z]+/
  end
  def upcase
    /[A-Z]+/
  end
  def ends_with_questionmark
    /[\?]$/
  end
end

Upvotes: 1

Views: 883

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110695

I find it difficult to comprehend what Bob#hey is doing. One possibility is to define some appropriately-named helper methods in the String class:

class String
  def contains_a_digit?()            !!(self =~ /\d/)        end
  def contains_no_digits?()          !self.contains_a_digit? end
  def contains_an_uppercase_char?()  !!(self =~ /[A-Z]/)     end
  def contains_no_lowercase_chars?() self !~ /[a-z]/         end
  def ends_with_questionmark?()      !!(self =~ /[\?]$/)     end
end

Note that (for example) self =~ /\d+/ and self =~ /\d/ are here interchangeable; both return a truthy value if and only if self contains at least one digit. Incidentally, the receiver, self, must be explicit here.

Aside to readers unfamiliar with !!: if truthy is a variable holding any value other than false or nil, !!(truthy) => true, !!(nil) => false and !!(false) => false. In other words, !! is a trick to convert truthy values to true and falsy values to false (which I've used merely to improve readability).

Let's try these methods:

str = 'U2d?'                     #=> "U2d?" 
str.contains_a_digit?            #=> true 
str.contains_no_digits?          #=> false 
str.contains_an_uppercase_char?  #=> true 
str.contains_no_lowercase_chars? #=> false 
str.ends_with_questionmark?      #=> true 

With Ruby 2.1+, if one is disinclined to monkey-patch the String class, one can use Refinements.

Now the method Bob#hey can be defined in natural way:

class Bob
  def hey(remark)
    case
    when remark.contains_no_digits? &&
         remark.contains_an_uppercase_char? &&
         remark.contains_no_lowercase_chars?
      'Whoa, chill out! (1st)'
    when remark.ends_with_questionmark?
      'Sure.'
    when remark.contains_a_digit? &&
         remark.contains_an_uppercase_char? &&
         remark.contains_no_lowercase_chars?
      'Whoa, chill out! (2nd)'
    else
      'Whatever.'
    end
  end
end

Let's try it.

bob = Bob.new
bob.hey("I PAID IN $US!")   #=> "Whoa, chill out! (1st)" 
bob.hey("What's that?")     #=> "Sure." 
bob.hey("I FLEW ON A 777!") #=> "Whoa, chill out! (2nd)" 
bob.hey("I give up.")       #=> "Whatever." 

Upvotes: 3

pguardiario
pguardiario

Reputation: 54992

I think in general more than 2 elses should be a case:

def hey remark
  case
    when remark =~ upcase && !(remark =~ downcase) && !(remark =~ numbers) then 'Whoa, chill out!'
    when remark =~ ends_with_questionmark then 'Sure.'
    when (remark =~ numbers) && (remark =~ upcase) && !(remark =~ downcase) then 'Whoa, chill out!'
    else 'Whatever.'
  end
end

But I also think those regexes need work, they probably don't do what you think.

Also you can change !(str =~ /re/) to str !~ /re/

Upvotes: 1

Toan Nguyen
Toan Nguyen

Reputation: 11591

You code can be rewritten like this

def hey(remark)
    if remark =~  /[A-Z]+/
      'Whoa, chill out!'
    elsif remark =~  /[\?]$/
      'Sure.'
    else
      'Whatever.'
    end 
  end

Upvotes: 0

Related Questions