Condense several conditional statement comparing the same value - ruby

I want to know if there is a way to condense this line of code:
elsif i == '+' || i == '-' || i == '/' || i == '*'

A case when control structure allows such a condensed line:
case i
when '+', '-', '/', '*' # <= condensed line of code
puts "operator!"
end

you could do
"+-/*".include?(i)

Similar to #Subash but you can also do this since:
#this returns the match string of i which is truthy or false if no match.
elsif "+-/*"[i]
if you want to return a boolean true or false you can also double bang
elsif !!"+-/*"[i] #true if matched, false if not
There are many variants of this in ruby, if you had a regex or some other type of string match you might also use
i = '/'
!!"+-/*".match(i) #true

Related

Is there a way to concatenate expressions in str.match in Ruby?

Am looking to match if a particular string has at least one digit, one upper, and one lower excluding symbols '%^&$#' and white spaces. So I am using str.match? I wonder if there is a one liner instead of && .. && .. concatenating expressions like data.match?(/\d+|[[:upper:]]|[[:lower:]]|\w+/) does not seem to be working.
def chkin(data)
if data.match?(/\d+/) \
&& data.match?(/[[:upper:]]+/) \
&& data.match?(/[[:lower:]]+/) \
&& data.match?(/\w+/) \
&& data.length >= 10
return true
else
return false
end
end
Also the \w+ alpha numeric does not work with: "2aA1b%%sdf3123!!" >> True when it should be false.
You can do that with a regular expression containing three positive lookaheads:
r = /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/
"abcDE27f".match?(r) #=> true
"abcde27f".match?(r) #=> false
"ABCDE27F".match?(r) #=> false
"abcDEfgh".match?(r) #=> false
You can add other requirements with additional positive lookaheads. For example, if the string must be between 8 and 10 characters in length, you could add
(?=.{8,10}\z)
If you only want the string to contain alphanumic characters add
(?=\p{Alnum}+\z)
or put \A\p{Alnum}+\z at the end of the regex.

how to return true or false when a string contains any letters A-Z or a-z?

I am familiar with how to check if a string contains a substring, and also familiar with how to check if a single letter is a number or a letter, but how would I go about checking a string for any letters?
def letters?(string)
# what do i do here?
end
# string could be anything from '111' to '1A2' to 'AB2589A5' etc...
string = '1A2C35'
if letters?(string) == true
# do something if string has letters
else
# do something else if it doesnt
end
The proper way to check if the string contains any letter, is to use \p{L} matcher. That way you’ll match
"ï" # in "naĩve"
as well as
"ç" # in Barça
The code would be:
def letters? string
!string[/\p{L}/].nil?
end
I think, you can try something like it:
def letters?(string)
string.chars.any? { |char| ('a'..'z').include? char.downcase }
end
If you don't wanna use regexp. This method return true if there are any letters in the string:
> letters? 'asd'
=> true
> letters? 'asd123'
=> true
> letters? '123'
=> false
This code checks if there are any letters from A to Z, the upper and lower case using regexp. If they are, it return true, false otherwise. This syntax a simple if else statement.
def letters?
self.match(/[A-Za-z]/) ? true : false
end
def letters?(string)
if (string =~ /[A-Za-z]/).to_s.length > 0
return true
else
return false
end
end

Ruby: .grep with regex

I'm using a regular expression to replace all '*' with '[A-Za-z0-9]*' except when the '*' is preceded by '\' like '\*'. How can I ignore the case '\*' ?
code:
puts Regexp.new(val.gsub(/^\*/,'[A-Za-z0-9]*')) =~ str ? 'true' : 'false'
You can use a Negative Lookbehind assertion here.
"foo\\* bar* baz* quz\\*".gsub(/(?<!\\)\*/, '[A-Za-z0-9]*')
# => 'foo\* bar[A-Za-z0-9]* baz[A-Za-z0-9]* quz\*'
You can do this by being more particular in your substitutions:
tests = [
"*.foo",
"\\*.foo"
]
tests.each do |test|
r = test.gsub(/(\\\*|\*)/) do |s|
case ($1)
when "\\*"
$1
else
"[A-Za-z0-9]*"
end
end
puts r
end
Results for me:
[A-Za-z0-9]*.foo
\*.foo
The first capture looks for \* specifically.

How to use .ord and .chr properly in a loop?

I am trying to make a function that takes a jumbled sequence of letters and returns English. For some reason, I can't get word = (word.ord-4).chr to work properly. The secret to the code is that the letters are shifted 4 slots backwards, which is why I'm converting it to an integer first, subtracting 4, and then turning it back to a string.
The loop also appears to be ignoring the fact that I told it to skip a word if it's any of those special characters. What am I doing wrong?
Any suggestions or sources that will bring me closer to solving this problem?
def north_korean_cipher(coded_mesage)
input = coded_mesage.split('') # splits the coded message into array of letters
input.each do |word|
word = (word.ord - 4).chr
if word == '#' || '#' || '$' || '%' || '^' || '&' || '*'
next
end
end
print input
end
north_korean_cipher('m^aerx%e&gsoi!')
You want a mapping like this:
input: abcdefghijklmnopqrstuvwxyz
output: wxyzabcdefghijklmnopqrstuv
Unfortunately your approach doesn't work for the first 4 letters:
("a".ord - 4).chr #=> "]"
("b".ord - 4).chr #=> "^"
("c".ord - 4).chr #=> "_"
("d".ord - 4).chr #=> "`"
I'd use String#tr. It replaces each occurrence in the first string with the corresponding character in the second string:
"m^aerx%e&gsoi!".tr("abcdefghijklmnopqrstuvwxyz", "wxyzabcdefghijklmnopqrstuv")
#=> "i^want%a&coke!"
There's also a "c1-c2 notation to denote ranges of characters":
"m^aerx%e&gsoi!".tr("a-z", "w-za-v")
#=> "i^want%a&coke!"
The documentation further says:
If to_str is shorter than from_str, it is padded with its last character in order to maintain the correspondence.
So it can be used to easily replace the "special characters" with a space:
"m^aerx%e&gsoi!".tr("a-z##$%^&*", "w-za-v ")
#=> "i want a coke!"
This:
if word == '#' || '#' || '$' || '%' || '^' || '&' || '*'
does not do what you expect it to do, because '#' as a condition will always be true. You can't compare objects like that. You should do something like
if word == '#' || word == '#' || word == '$' || word == '%' || word == '^' || word == '&' || word == '*'
You can write it in a more succinct way by asking:
if %w(# # $ % ^ & *).include? word
Which checks if word is in the collection of options...

Testing Regex in If, Else Statement

I think I'm close, but the regex isn't evaluating. Hoping someone may know why.
def new_title(title)
words = title.split(' ')
words = [words[0].capitalize] + words[1..-1].map do |w|
if w =~ /and|an|a|the|in|if|of/
w
else
w.capitalize
end
end
words.join(' ')
end
When I pass in lowercase titles, they get returned as lowercase.
You need to properly anchor your regular expression:
new_title("the last hope")
# => "The last Hope"
This is because /a/ matches a word with an a in it. /\Aa\Z/ matches a string that consists entirely of a, and /\A(a|of|...)\Z/ matches against a set of words.
In any case, what you might want is this:
case (w)
when 'and', 'an', 'a', 'the', 'in', 'if', 'of'
w
else
w.capitalize
end
Using a regular expression here is a bit heavy handed. What you want is an exclusion list.
This is called titleize, and is implemented like this:
def titleize(word)
humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
end
Se the doc.
If you want fancy titlezing, check out granth's titleize
Your regular expression should be checking the whole word (^word$). Anyway, isn't more simple to use Enumerable#include?:
def new_title(title)
words = title.split(' ')
rest_words = words.drop(1).map do |word|
%w(and an a the in if of).include?(word) ? word : word.capitalize
end
([words[0].capitalize] + rest_words).join(" ")
end

Resources