1

I have a quick question on Ruby syntax: I am trying to write a program where it takes a string and replaces substrings with a different substring IF it contains the initial substring. Here is my code:

print "Thtring, pleathe!"

user_input = gets.chomp

if user_input.include? "s" || "S" || "cy" || "ce" || "ci"

    user_input.gsub!(/s/, "th")
    user_input.gsub!(/S/, "Th") 
    user_input.gsub!(/cy/, "th") 
    user_input.gsub!(/ce/, "th") 
    user_input.gsub!(/ci/, "th") 
else

    puts "No 's' found!"
end

puts "#{user_input}!" 

This works fine until I have a sentence that doesn't contain "s" in it. Then it just prints the original string with no changes. Is there a logical operator that means AND/OR in Ruby? To replace all the ORs (||) with. And if there isn't, how would I rewrite this to make it work?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Turbo
  • 29
  • 3

5 Answers5

3

What you're doing now is effectively

if user_input.include?("s") || "S" || "cy" || "ce" || "ci"

(Note the added parentheses.) Also, you can't do something like that in Ruby - the || operator only works for boolean expressions.

You have two alternatives:

if user_input.include?("s") || user_input.include?("S") || user_input.include?("cy") || user_input.include?("ce") || user_input.include?("ci")

or the much neater

if user_input =~ /s|S|cy|ce|ci/
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Weetu
  • 1,761
  • 12
  • 15
2

It is just:

print "Thtring, pleathe!"
user_input = gets.chomp

puts "No 's' found!" unless user_input.gsub!(/s|S|cy|ce|ci/, 'th')

puts "#{user_input}!"

What works, because gsub! returns nil if nothing is replaced.

spickermann
  • 100,941
  • 9
  • 101
  • 131
1

There's some seemingly little-known magic that gsub can do using a regexp pattern and a hash.

Starting with a hash of the target strings to be found and their replacements:

substitutions = {
  's' => "th",
  'S' => "Th",
  'cy' => "th",
  'ce' => "th",
  'ci' => "th",
}

We can build a regular expression based on the keys (target strings):

substitution_pattern = Regexp.union(substitutions.keys) # => /s|S|cy|ce|ci/

And pass the pattern and the hash to gsub:

'String please!'.gsub(substitution_pattern, substitutions) # => "Thtring pleathe!"

Here are the individual tests:

's'.gsub(substitution_pattern, substitutions) # => "th"
'S'.gsub(substitution_pattern, substitutions) # => "Th"
'cy'.gsub(substitution_pattern, substitutions) # => "th"
'ce'.gsub(substitution_pattern, substitutions) # => "th"
'ci'.gsub(substitution_pattern, substitutions) # => "th"

The pattern being built by Regexp.union isn't especially good, because all it really does is join('|'). We can easily create a smarter pattern:

substitution_pattern = /[Ss]|c[eiy]/
'String please!'.gsub(substitution_pattern, substitutions) # => "Thtring pleathe!"

There are several answers on Stack Overflow where I use a Perl module called Regexp::Assemble to generate very complex patterns for this purpose. Check out these Regexp::Assemble answers for more information. In particular, "Is there an efficient way to perform hundreds of text substitutions in Ruby?" will show you all you need to know to do this.

Community
  • 1
  • 1
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • This one is pretty golden. Though I second the little-known aspect here. I've personally never seen this one, although in retrospect and checking the docs you'd expect this to be more popular – Stefan Dorunga Dec 11 '14 at 14:56
  • This is getting into some of the deeper magic inside Ruby. I've found that a lot of developers tend to not want to dig into languages and are content with the surface stuff, but the real power of a language is not on the surface. With a proper set of keys, or a well constructed regex pattern, this can offload a huge amount of processing to the regex engine and lookups into a hash, both of which can be extremely fast. – the Tin Man Dec 11 '14 at 15:03
0

An easy way of doing it is to just use a regexp. You could do

if user_input.match(/(s|S|cy|ce|ci)/)
  ...
else
  ...
end

There are also, as always, other options such as in Milan Vladimirovic's answer:

if user_input =~ /(s|S|cy|ce|ci)/

as well as Tin Man's suggestion

if user_input[/(s|S|cy|ce|ci)/]

Use whichever you prefer since they are for this scenario functionally equivalent. The other thing is you could just run the gsubs anyway since they won't cahnge anything that isn't there, unless you really want to output the "No 's' found!"

Stefan Dorunga
  • 679
  • 6
  • 18
0

I guess you have a problem with operator binding priorities. Wrapping the ORs (||) in brackets (include?('s') || include?('S')) should do the trick and make your code work.

You could also write it as a regular expression:

if user_input =~ /s|S|cy|ce|ci/
  user_input.gsub!(/s/, "th")
  user_input.gsub!(/S/, "Th") 
  user_input.gsub!(/cy/, "th") 
  user_input.gsub!(/ce/, "th") 
  user_input.gsub!(/ci/, "th")
else
  puts "No 's' found!"
end
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Milan Köpke
  • 1,133
  • 7
  • 8