Not understanding the Boolean Logic to my method - ruby

I write the method capitalized(word) to be funneled into the .each function.
i use bang! to make the function work correctly but i don't understand why its working correctly.
The part that is really throwing me off is the !capitalized word line if statement. I don't understand the logic and how the method returns the answers that it does.
!false returns false in the method... why? And !true it returns true.
Whats the best way to understand this concept.
I have tried taking out the bang and putting it back in to see its effect.
And it just confuses me. Thank you for your help.
# A name is valid is if satisfies all of the following:
# - contains at least a first name and last name, separated by spaces
# - each part of the name should be capitalized
#
# Hint: use str.upcase or str.downcase
# "a".upcase # => "A"
def is_valid_name(str)
name = str.split(' ')
if name.length < 2
return false
end
name.each do |word|
if !capitalized(word)
return false
end
end
return true
end
def capitalized(word)
if word[0] == word[0].upcase && word[1..-1]== word[1..-1].downcase
return true
else
return false
end
end
puts is_valid_name("Kush Patel") # => true
puts is_valid_name("Daniel") # => false
puts is_valid_name("Robert Downey Jr") # => true
puts is_valid_name("ROBERT DOWNEY JR") # => false

The purpose of the part of the code you struggling with, is to check whether all of the elements of the name are capitalised. There are two ways you can approach that problem:
Find any element that is not capitalised.
If such element exists, that means name is not valid, so method is_valid_name should return false. This is the approach used in you code:
def is_valid_name(str)
#...
name.each do |word| #1
if !capitalized(word) #2
return false #3
end
end
return true #4
end
Iterate over every part of the name
Check if word is capitalized. We want to break only when we find invalid (not capitalized) word. That occurs when capitalized(word) returns false: so condition if !capitalized(word) is actually if !false, which is equal to if true.
We entered, that means our word is not valid, so we return false from is_valid_name method.
Check if all elements are capitalized
This is more straightforward solution. We just want to check, if method capitalized(word) returns true for all of the elements of the name.
To achieve that, we can use method all?, which returns true if condition in the block returns true for every element; otherwise false. So we can replace all the code above with single line:
name.all?{|word| capitalized(word) }
Final implementation of the validation method can look like that:
def is_valid_name(str)
name = str.split(' ')
name.length < 2 && name.all?{|word| capitalized(word)}
end
Hope it helps you!

!false returns false in the method... why?
I'm assuming you mean this one:
if !capitalized(word)
return false
end
"If capitalize returns false, why do we return false", this question? Because if a word is not capitalized, the name is invalid. That's the given business logic.

Related

Check whether string match or not

I have a string and an array with some strings.
as below
hostname = TETDC3DBE01
Array = ['WEB','APP','STR','DBE']
I want to find whether that hostname match with any of the array element or not?
When I'm trying with below code getting output
no
no
no
no
Here is loop repeating each and every element on array. I want check that hostname with single check on array the produce the output either yes or no only.
Array.each do |x|
if hostname.match(x)
puts "yes"
else
puts "no"
end
end
Given this fixed Ruby code:
hostname = 'TETDC3DBE01'
array = ['WEB','APP','STR','DBE']
Where if you want to find all elements in array that match as a substring of hostname your code should work. The more minimal matching system is probably:
array.select { |x| hostname.match(x) }
# => ["DBE"]
Using a tool like puts to produce output isn't always very useful because that "yes" or "no" text can't be acted upon by more code. Try and think of Ruby programs as a chain of transformations, where this selects all matches, and later you can print them, like this:
puts array.select { |x| hostname.match(x) }.join(',')
# => DBE
Check out Array#any? method.
It passes each element of the collection to the given block. The method returns true if the block ever returns a value other than false or nil. If the block is not given, Ruby adds an implicit block of { |obj| obj } that will cause any? to return true if at least one of the collection members is not false or nil.
If instead a pattern is supplied, the method returns whether pattern === element for any collection member.
In your case:
hostname = 'TETDC3DBE01'
['WEB','APP','STR','DBE'].any? do |x|
hostname.match(x)
end
or even if you actually mean equal by match:
hostname = 'TETDC3DBE01'
['WEB','APP','STR','DBE'].any?(hostname)
Lets take your code to fix it.
hostname = "TETDC3DBE01"
arr = ['WEB','APP','STR','DBE']
arr.each do |x|
if hostname.match?(x)
puts "yes"
else
puts "no"
end
end
match gives array of result and
match? gives you true or false value
I wouldn't use regexp in this case. A simple String#include? is probably faster. Furthermore any? will return true if any of the elements in the array leads is matching.
hostname = 'TETDC3DBE01'
array = ['WEB','APP','STR','DBE']
array.any? { |x| hostname.include?(x) }
#=> true
Regular expression made real easy:
hostname = "TETDC3DBE01"
array = ['WEB','APP','STR','DBE']
re = Regexp.union(array)
hostname.match?(re) # => true

Calling method isn't returning string

I created a method to count a substring 'e' in a string passed as an argument. If there isn't a substring 'e' in the string, it should return "There is no \"e\"." I am trying to achieve this:
How many times 'e' is in a string.
If given string doesn't contain any "e", return "There is no "e"."
if given string is empty, return empty string.
if given string is nil, return nil.
This is my code:
def find_e(s)
if !s.include?("e")
"There is no \"e\"."
elsif s.empty?
""
else s.nil?
nil
end
s.count("e").to_s
end
find_e("Bnjamin")
It skips the if statement and it still uses the method count. Why is this?
To achieve what you want you could move your string.count to the else statement in your if, because actually you're making your method return the quantity of e passed in the count method over your string, but what happens inside the if isn't being used:
def find_e(s)
if s.nil?
nil
elsif s.empty?
''
elsif !s.include?("e")
"There is no \"e\"."
else
s.count("e").to_s
end
end
p find_e("Bnjamin") # => "There is no \"e\"."
p find_e("Benjamin") # => "1"
p find_e(nil) # => nil
p find_e('') # => ""
And also your validations must be in order, first check nil values, then empty values, and then the rest, if you don't then you'll get some undefined method ___ for nil:NilClass errors.
You might have a hard time using the method you wrote. In the next method, you'll need a new case statement to test if find_e returned nil, an empty string, a string with a number or "no e".
This method would be a bit more consistent:
def count_e(string_or_nil)
count = string_or_nil.to_s.count("e")
if count == 0
"There is no \"e\"."
else
count
end
end
puts count_e("Covfefe")
# 2
puts count_e("Bnjamin")
# There is no "e".
puts count_e("")
# There is no "e".
puts count_e(nil)
# There is no "e".
But really, if there's no e in the input, just returning 0 would be the most logical behaviour.
You need to put your count method in a branch of the if/else statement, or else it will be evaluated last every time. Without an explicit return statement Ruby will return the last statement, so putting the method outside the if/else branch on the last line guarantees it will always be hit. Also, nil can be converted to an empty string by calling #to_s, so you can remove one of your branches by converting s.to_s, calling empty? and returning s
def find_e(s)
if s.to_s.empty?
s
elsif !s.include?("e")
"There is no \"e\"."
else
s.count("e").to_s
end
end
If you just return 0 whether you get nil, an empty string, or a string without e, you can make it one line
def find_e(s)
s.to_s.count("e").to_s
end
If it were me I'd probably return an Integer, which can always be converted to a String later. puts and "#{}" will implicitly call to_s for you anway. Then you can use that integer return in your presentation logic.
def count_e(input)
input.to_s.count("e")
end
def check_for_e(input)
count = count_e(input)
count > 0 ? count.to_s : "There's no \"e\"."
end
check_for_e("Covfefe") # => "2"
check_for_e("Bnjamin") # => "There's no \"e\"."
check_for_e(nil) # => "There's no \"e\"."
check_for_e("") # => "There's no \"e\"."
In Ruby, methods return the last statement in their body. Your method's last statement is always s.count("e").to_s, since that lies outside of the if statements.

I need to write a regex in Ruby that returns true / false

I am trying to write a regex that takes a word and returns true for words starting with a vowel and returns false for words starting with a consonant. I have never written regex before, and I'm a little confused on how to write the expression. This is what I have so far:
def starts_with_a_vowel?(word)
if word.match(/\A+[aeiou]/) == true
return true
else
return false
end
end
Edit: So if word = "boat" , expression should return false. If word = "apple", expression should return true.
word.match? /\A[aeiou]/i is literally all you need. (ruby >= 2.4)
It matches the beginning of the string \A followed by a vowel [aeiou] in a case-insensitive manner i returning a bool word.match?
Before ruby 2.4 you have to use word.match and convert it to a bool, which is easiest done by logically negating it twice with !!
EDIT:
OK.. So.. I never tested the code I formerly wrote.. it was meant only as some suggestion how to use the match with regex, but it not worked at all.. (that code snippet is now attached to the end of my answer fyi)
this here should be the working one:
def starts_with_a_vowel?(word)
!!word.capitalize.match(/\A+[AEIOU]/)
end
..but how it was mentioned by Eric Duminil here below in comments, the method/function is
not needed
!!word.capitalize.match(/\A+[AEIOU]/) can be used directly..
it returns true or false
but there are surely other (maybe better) solutions too..
..and here is the NOT working code, which I formerly wrote:
def starts_with_a_vowel?(word)
return word.match(/\A+[aeiou]/).length > 0
end
..the match method returns nil when not match and because nil has no length method defined, it raises NoMethodError
Here are a couple of ways to do this.
#1
word = 'Ahoy'
!!(word[0] =~ /[aeiou]/i)
#=> true
word = 'hiya'
!!(word[0] =~ /[aeiou]/i)
#=> false
The regex reads, "match a vowel, case indifferently (/i)". !! converts a thruthy value to true and a falsy value (nil or false) to false:
!!0 = !(!0) = !(false) = true
!!nil = !(!nil) = !(true) = false
#2
word = 'Ahoy'
(word[0] =~ /[aeiou]/i) ? true : false
#=> true
word = 'hiya'
(word[0] =~ /[aeiou]/i) ? true : false
#=> false
You're doing a lot of extra work for no reason. First, you don't need to check for equality with true; just if *expression* does the trick.
But in this case you don't need if at all. A regex match already returns a value that can be interpreted as a Boolean. =~ returns the index of the match as an integer (which is "truthy"); String#match returns a MatchData object (which is also truthy). Everything in Ruby is truthy except false and nil, and nil is what both =~ and String#match return if there's no match. So all you have to do is turn the result of one of those into the corresponding Boolean value, which you can do with !!. For example:
def starts_with_a_vowel? word
!!(word =~ /^[aeiou]/)
end
That !! is read "not not", by the way. The ! operator by itself treats its argument as Boolean and returns its opposite as an actual Boolean; that is !(some truthy value) returns false (not nil), and !(some falsey value) returns true (not just some truthy value). So applying ! twice turns any truthy value into true and any falsey value (false or nil) into false.
Do you have to use a regular expression? Just asking cause Ruby already provides String#start_with?
vowels = %w(a e i o u)
"boat".start_with?(*vowels)
# => false
"apple".start_with?(*vowels)
#=> true
In Ruby, you almost never need anything to return true or false. For boolean logic and if/case/unless statements, truthy/falsey are good enough. Also, don't forget to use case-insensitive Regex (with //i). A is a vowel :
class String
def starts_with_a_vowel?
self =~ /\A[aeiou]/i
end
end
if "Airplane".starts_with_a_vowel?
puts "Indeed"
end
#=> "Indeed"
If for some reason you really need true/false :
class String
def starts_with_a_vowel?
!(self =~ /\A[aeiou]/i).nil?
end
end

Why is this ruby test failing?

I am doing some of the kata challenges on codewars:
This is the challenge: An isogram is a word that has no repeating letters, consecutive or non-consecutive. Implement a function that determines whether a string that contains only letters is an isogram. Assume the empty string is an isogram. Ignore letter case.
I have the following piece of code:
def is_isogram(string)
string.downcase!
('a'..'z').each do |letter|
return string.count(letter) <= 1 || string.length == 0 ? true : false
end
end
with the following tests:
Test.assert_equals(is_isogram("Dermatoglyphics"), true )
Test.assert_equals(is_isogram("isogram"), true )
Test.assert_equals(is_isogram("aba"), false, "same chars may not be adjacent" )
Test.assert_equals(is_isogram("moOse"), false, "same chars may not be same case" )
Test.assert_equals(is_isogram("isIsogram"), false )
Test.assert_equals(is_isogram(""), true, "an empty string is a valid isogram" )
My code fails on the fourth test. Could someone please shed some light on what I am doing wrong and how I can get the test to pass?
You are returning from the method at the end of the first iteration. Thus, your code has nothing to do with isogram. Your code will check whether the first character in the range (i.e., "a") is repeated or not.
The first two examples only have one "a", so they return true. The third has more than one "a", so it returns false. The fourth has no "a", so it returns true.
To get your code to work, change it to:
def is_isogram(string)
string.downcase!
('a'..'z').each do |letter|
return false if string.count(letter) > 1
end
return true
end
But a more Rubyish way to write it is:
def is_isogram(string)
string = string.downcase
('a'..'z').none?{|letter| string.count(letter) > 1}
end
try this
def is_isogram(string)
string.downcase.chars.uniq == string.downcase.chars
end

trouble with this case statement Ruby

Can someone help me understand how to write this case statement properly its not working and as a NOOB I have no idea how to fix it:
def hide_link?(link, mailing)
case link
when 'edit' && ['sent', 'sending', 'archived'].include?(mailing.status)
return true
when 'send_schedule' && ['sent', 'sending', 'archived'].include?(mailing.status)
return true
when 'archive' && ['archived'].include?(mailing.status)
puts "I should be in here"
return true
else 'dashboard' && ['sending', 'draft'].include?(mailing.status)
return true
end
end
Basically I want to return true when the link matches certain criteria.
I believe that if link doesn't match these criterias the method should return false. Thus:
def hide_link?(link, mailing)
case link
when 'edit'
['sent', 'sending', 'archived'].include?(mailing.status)
when 'send_schedule'
['sent', 'sending', 'archived'].include?(mailing.status)
when 'archive'
puts "I should be in here"
['archived'].include?(mailing.status)
when 'dashboard'
['sending', 'draft'].include?(mailing.status)
else
false
end
end
The construction [...].include?(mailing.status) has result true or false which will be returned as a result of hide_link? method.
Remove return.
link = "fred"
case link
when "fred"
true
else
false
end
case will return the value itself which will then be passed to the method.
Refactor of megas's version:
def hide_link?(link, mailing)
statuses_to_hide = case link
when 'edit', 'send_schedule'
%w{sent sending archived}
when 'archive'
%w{archived}
when 'dashboard'
%w{sending draft}
else
[]
end
statuses_to_hide.include?(mailing.status)
end
The conditions in the case statement all follow the same form, which suggest that there is an opportunity to eliminate some repetition, and to separate policy from implementation. The policy is the set of conditions under which the link should be hidden:
WHEN_TO_HIDE_LINK = [
['edit', %w(sent sending archived)],
['send_schedule', %w(sent sending archived)],
['archive', %w(archived)],
['dashboard', %w(sending draft)],
]
The implementation is the code that applies the policy:
def hide_link?(link, mailing)
WHEN_TO_HIDE_LINK.any? do |link_value, mailing_statuses|
link_value == link && mailing_statuses.include?(mailing.status)
end
end
Explanations below the fold.
%w
%w is a way to specify a list of strings without typing all those quotes and commas. This:
%w(sent sending archived)
is equivalent to this:
['sent', 'sending', 'archived']
any?
Enumerable#any? passes each element of the array to the block (the bit between the do and the end). If the block ever returns truthy, then the result of any? is true; otherwise, the value of any? is false.
array decomposition
Did you notice that although each element of WHEN_TO_HIDE_LINK is an array, the block passed to any? does not take an array? You might expect that you'd have to do this:
WHEN_TO_HIDE_LINK.any? do |when_to_hide|
link_value = when_to_hide[0]
mailing_statuses = when_to_hide[1]
...
but Ruby will decompose array into parts for you. Here's one way to do it:
WHEN_TO_HIDE_LINK.any? do |when_to_hide|
link_value, mailing_statuses = when_to_hide
...
When there is an array on the right side of the = and comma-separated variables on the left, Ruby decomposes the array into its elements and assigns them to the variables separately.
But Ruby can make things even easier:
WHEN_TO_HIDE_LINK.any? do |link_value, mailing_statuses|
...
This is equivalent to either of the preceding two fragments.

Resources