Reputation: 29
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?
Upvotes: 1
Views: 114
Reputation: 160551
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.
Upvotes: 1
Reputation: 679
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 gsub
s anyway since they won't cahnge anything that isn't there, unless you really want to output the "No 's' found!"
Upvotes: 0
Reputation: 1133
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
Upvotes: 0
Reputation: 1773
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/
Upvotes: 3
Reputation: 106882
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.
Upvotes: 2