Why is this ruby test failing? - ruby

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

Related

Not understanding the Boolean Logic to my method

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.

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

Function to determine if a number is repeated in a year is true or false, seems to not like the 1800's?

I'm trying to write a function whether a number repeats, it seems like the function works most of the time but not reliably. I'm not sure what the problem with my code is, code follows:
def repeat?(year)
y_arr = year.to_s.split('').map(&:to_i)
y_arr.each do |i|
y_arr.each do |j|
if (i != j) && (y_arr[i] == y_arr[j])
return true
end
end
end
return false
end
puts(repeat?(1702))
puts(repeat?(1997))
puts(repeat?(2001))
puts(repeat?(1859))
output for the above ->
false
true
true
true (should be false?)
changed code to:
def repeat?(year)
y_arr = year.to_s.split('').map(&:to_i)
for i in 0..y_arr.length
for j in 0..y_arr.length
if (i != j) && (y_arr[i] == y_arr[j])
return true
end
end
end
return false
end
Works now! Thanks for your responses
You're making it much more complex than it needs to be. This can be done with a one-liner.
/(\d)\1+/ =~ number.to_s
The \1+ is known as a backreference in regex. It references what is captured between the parentheses which in this case is a digit.
This will find repeated digits.
It can also be done by checking whether digits are unique or not.
def repeat?(year)
not (year.to_s.chars == year.to_s.chars.uniq)
end
puts(repeat?(1702))
#=> false
puts(repeat?(1997))
#=> true
puts(repeat?(2001))
#=> true
puts(repeat?(1800))
#=> true

why are my returns messing up my results?

The objective of this piece of code (it's part of a larger code) is to determine if a year has duplicate numbers in it. Here is my code:
def no_repeat?(year)
year = year.to_s
string = ''
year.each_char{|i| string << year[i] unless string.include?(year[i])}
year.length == string.length ? (return false) : (return true)
end
puts no_repeat?(1993)
It ALWAYS returns true, and I can't see why that is happening. I have tried expanding the ternary into a full if statement...still returns true. I have tried writing this whole method out as a while loop (with two indexes that compare one index to the other)
def no_repeat?(year)
year = year.to_s
i = 0
while i < year.length
i2 = i + 1
while i2 < year.length
if year[i] == year[i2]
return false
else
return true
end
i2 += 1
end
i += 1
end
...still returns true. I have tested each thing independently and they all work fine until I put the returns in. What is it about the returns? I need fresh eyes on it.
The way you've structured the ternary is incorrect. Since your method is attempting to ensure nothing is repeated, it should return true when the == is true. A ternary itself is intended to return a value, not really to execute an expression like (return false) inside its result. That works, but is unconventional to being practically non-existent.
The ternary should look like
return year.length == string.length ? true : false
Which can of course be simplified because the == expression already returns a boolean.
return year.length == string.length
Next, your use of year[i] isn't quite right. String#each_char is assigning the character value into i, so you can use i directly. It appears that the way you've used it actually does work, but that's not how the iterator variable i is meant to be used.
This makes your method into:
def no_repeat?(year)
year = year.to_s
string = ''
# i represents the character in this iteration
# so you may just directly reference i here
year.each_char{|i| string << i unless string.include?(i)}
# If the lengths match, return true because there's no repeating character
return year.length == string.length
# You could omit the 'return' keyword too which is preferred by convention
# since Ruby returns the last expression's value implicitly
# year.length == string.length
end
You have the true and false statements flipped. Otherwise the code works.
This works:
def no_repeat?(year)
year = year.to_s
string = ''
year.each_char{|i| string << year[i] unless string.include?(year[i])}
year.length == string.length ? (return true) : (return false)
end
no_repeat?(1993) # => false
no_repeat?(2015) # => true
However there are many ways that you should improve this code. The return keyword is rarely used in Ruby. In fact, it is completely superfluous in your example. These two methods are equivalent:
# with `return`
def no_repeat?(year)
year = year.to_s
string = ''
year.each_char{|i| string << year[i] unless string.include?(year[i])}
year.length == string.length ? (return true) : (return false)
end
# without `return`
def no_repeat?(year)
year = year.to_s
string = ''
year.each_char{|i| string << year[i] unless string.include?(year[i])}
year.length == string.length
end
Second, using a negative ("no") in a method name makes code hard to follow. I suggest flipping the logic and calling the method repeat? or even better repeat_chars?.
Finally, there are more concise ways to express the logic you have written using built-in Ruby methods. Here is one alternative implementation (I'm sure other Rubyists can chime in with even more elegant solutions):
def repeat_chars?(year)
year = year.to_s
year.length != year.chars.uniq.length
end

Resources