Calling method isn't returning string - ruby

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.

Related

Why is this ruby method not terminating at return?

newbie ruby question
code challenge: string collapse: remove all adjacent duplicates in a given string. Note: removal of adjacent duplicates can create new adjacent duplicates, you must remove these as well.
I'm pretty sure I solved this with simple recursion but when I walk through this step by step in a debugger the code doesn't terminate when it reaches the return line and begins adding then removing letters to the string until finally terminating.
for example
zzzxaaxy => zxaaxy => zxxy => zy => zxxy => zy => zxxy =>
^ code hits return line here and should stop but instead continues
def same_char_collapse(str)
ar = str.split('')
ar.map.each_with_index do |char1, idx|
char2 = ar[idx+1]
if char1 == char2
ar.delete_at(idx)
ar.delete_at(idx)
same_char_collapse(ar.join(''))
end
end
return ar.join('')
end
The return keyword does not mean "terminate instantly and return my value", it means "return to the method that called me."
So when you call your method and it returns "zxxy" instead of "zy", it is because it is just returning the result of ar.join() in the first call to same_char_collapse.
def same_char_collapse(str)
ar = str.split('')
ar.map.each_with_index do |char1, idx|
char2 = ar[idx+1]
if char1 == char2
ar.delete_at(idx)
ar.delete_at(idx)
# same_char_collapse(ar.join('')) doesn't change the value we are returning.
# you could delete this line and the method would still return the same thing 'zxxy'
same_char_collapse(ar.join(''))
end
end
# ar's value is zxxy from the loop since the same_char_collapse(ar.join('')) line
# doesn't change ar. only the ar.delete_at(idx) lines modified ar.
return ar.join('')
end
same_char_collapse('zzzxaaxy') # first call to same_char_collapse
Essentially, if you want to fix your solution, you should make sure you are using the value produced in the recursive call.
thanks for the help and after lots of trial and error I eventually worked
out a solution
def dupes?(word)
word.each_char.with_index do |char, idx|
if char == word[idx+1]
return true
end
end
return false
end
def removeDupe(word)
ar = word.split('')
ar.map.with_index do |char,idx|
if char == ar[idx+1]
ar.delete_at(idx)
ar.delete_at(idx)
end
end
return ar.join('')
end
def same_char_collapse(str)
if dupes?(str)
same_char_collapse(removeDupe(str))
else
return str
end
end

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.

include works with if but not else

I doing a Ruby botcamp. I'm supposed to write code that replaces all user input of 's' with 'th' so it reads like Daffy Duck is speaking. If I enter an s it will be replaced with th. That works! But If I don't enter an 's' it's supposed to print that none were included in my elsif statemnt. Instead I'm getting the error 'undefined method `include?' for nil:NilClass'. Other than that error, the interpretor is telling me the code is good.
print "Input a string: "
user_input=gets.chomp.downcase!
if user_input.include?"s"
user_input.gsub!(/s/, "th")
puts "Your string is #{user_input}!"
elsif
puts "There are no s's in your string!"
end
Any ideas on what I need to change?
You need to be careful with built-in ruby methods that end with an exclamation point (!). A lot of them will return nil if no changes were made:
'test'.downcase! # => nil
'Test'.downcase! # => "test"
Since you are assigning the result to a variable, there's no need to use the exclamation point method, since those modify in-place, you can just use the normal downcase method.
'test'.downcase # => "test"
You also later on have an elsif with no condition, that should probably just be an else. It's actually executing the first line of the "body" of the elsif as the conditional:
if false
puts "a"
elsif
puts "b" # recall that `puts` returns `nil`
puts "c"
else
puts "d"
end
This results in
b
d
being output

Ruby puts command in modules returning nil

i am working though LearnTocodethehardway.com http://ruby.learncodethehardway.org/book/ex25.html
On ex25. In the example there is a module that has a bunch of methods that return or print values. The Print_last_word method when supplied with an array of strings just puts nil. it does this even in his example output. My question would then be why?
To be precise, it doesn't puts nil - it puts the last word and returns nil. Here's the example output:
>> Ex25.print_last_word(words)
wait. # <- this is the output
=> nil # <- this is the return value
puts always returns nil.
UPDATE
There seems to be a bug in print_first_word:
module Ex25
def Ex25.print_first_word(words)
word = words.pop(0)
puts word
end
end
Ex25.print_first_word(["foo", "bar", "baz"])
#=> nil
This is because ["foo", "bar", "baz"].pop(0) returns an empty array and puts [] just returns nil without printing anything.
A working implementation (in the exercise's style) could look like this:
module Ex25
def Ex25.print_first_word(words)
word = words.shift
puts word
end
end
To make it more easy to understand:
"puts" never is used for its return value. It is used for its side effect if you wanted a method that would return a word, you would then do something with that word.
words = ["apple", "cucumber", "apple"]
def give_me_first_word(array_of_words)
array_of_words.first
end
variable_to_be_used_later = give_me_first_word(words)
This function would return "apple"(and in this case the variable would be assigned "apple"), but it would puts nothing to the screen. This value would then be used in another program or function.
If you puts a value, you would then not need to return it to any other program as it's already served its purpose. It's actually intuitive because if puts also returned the value, it would be doing two things. The counterpart to "puts" would be "return" that simply returns the value and does not display it to the screen.

if method_one returns value return value, else try method_two in ruby

How do I say if method_one returns a value, then break, else try method_two?
def ai_second_move(board)
p "2nd move called"
# TODO - how to say if method_one gives me a value, break, else method_two
method_one(board)
method_two(board)
end
Most Ruby way of writing this would be:
method_one(board) || method_two(board)
Ruby executes the right-hand side of || only if the left hand side evaluated to false (meaning it returns nil or false) and then the result of this expression would be that of the method_two
using if -
method_two(board) if method_one(board).nil?
using unless -
method_two(board) unless !method_one(board).nil?
using ternary -
# This evaluates if (method_one(board) returns nil) condition. If its true then next statement is method_two(board) else return is executed next.
method_one(board).nil? ? method_two(board) : return
This would work as well:
method_one(board) and return
The return statement is executed only if method_one(board) returns a truthy value.
You need to use return. break is for loops.
def ai_second_move(board)
p "2nd move called"
return if !!method_one(board)
method_two(board)
end
An other fun way would be
def ai_second_move(board)
p "2nd move called"
!!method_one(board) || method_two(board)
end

Resources