Why one solution prints true when the other prints false? - ruby

I have two solutions to reverse a string in Ruby. One prints true while the other prints false, however, both print out the response I want.
Why does one say it's false even though it results in the same answer as the solution that prints true?
Here are the solutions and the tests:
def reverse(string)
new = ""
i = 0
length = string.length
while i < length do
new = new.to_s + string[-1, 1].to_s
string.chop!
if i >= string.length
break
end
end
puts new
end
def secondreverse(string)
new = ""
i = 0
length = string.length
while i < length do
new = string[i] + new
i += 1
end
return new
end
These are tests to check that the code is working. After writing your solution, they should all print true.
puts("\nTests for #reverse")
puts("===============================================")
puts(
'secondreverse("abc") == "cba": ' + (secondreverse("abc") == "cba").to_s
)
puts(
'secondreverse("a") == "a": ' + (secondreverse("a") == "a").to_s
)
puts(
'secondreverse("") == "": ' + (secondreverse("") == "").to_s
)
puts("===============================================")

In your #reverse function, you are returning puts new when you should just be returning new.
As you can see from the example below, puts returns nil after it prints to the screen:
irb(main): puts 'test'
test
=> nil
If you change puts new to just new, it works as you expect.
Aside
You don't need to use explicit return calls. In Ruby, the last line executed will be returned, so you can replace this in both methods:
return new
with:
new

The problem is that in the reverse method, you are printing the value to stdout using the puts method but you are not returning it (your method returns nil instead). When you compare nil == "cba" it returns false. You have to return the new variable:
def reverse(string)
new = ""
i = 0
length = string.length
while i < length do
new = new.to_s + string[-1, 1].to_s
string.chop!
if i >= string.length
break
end
end
new
end

Related

Ruby: Reverse method from scratch

I'm working on a coding challenge practice problem that is asking me to put together a method that reverses a string and have come up with:
def reverse(string)
idx = 1
array_s = []
while idx <= string.length
array_s.push(string[string.length - idx])
idx = idx + 1
end
new_string = array_s.join("")
puts new_string
end
When I run the test given at the end,
puts(
'reverse("abc") == "cba": ' + (reverse("abc") == "cba").to_s
)
puts(
'reverse("a") == "a": ' + (reverse("a") == "a").to_s
)
puts(
'reverse("") == "": ' + (reverse("") == "").to_s
)
it seems to show that the strings reverse, but it still prints false. Is there anything I'm missing in the method I wrote? Thanks.
Your function is working as expected, it is the last line that is causing you issues.
puts new_string
puts returns nil, ruby uses implicit returns unless you explicitly say return, so it returns the result from the last line of code, in this case is nil.
If you just do new_string or return new_string, it will work.
Or if you want to print it to the screen and return it as the result, you can do
p new_string
davidhu2000 has answered your question correctly but i would suggest refactoring the code:
f = "forward"
puts f.reverse
# drawrof
of if you really want:
def reverse(string)
return string.reverse
end

Why does `String#delete` return an error in my ruby code?

The goal is to take a string and return the most common letter along with it's count. For string 'hello', it would return ['l', 2].
I've written the following:
def most_common_letter(string)
list = []
bigcount = 0
while 0 < string.length
count = 0
for i in 0..string.length
if string[0] == string[i]
count += 1
end
end
if count > bigcount
bigcount = count
list = (string[0])
string.delete[string[0]]
end
end
return [list,bigcount]
end
I get the following error:
wrong number of arguments (0 for 1+)
(repl):14:in `delete'
(repl):14:in `most_common_letter'
(repl):5:in `initialize'
Please help me understand what I'm doing wrong with the delete statement, or what else is causing this to return an error.
I have a solution done another way, but I thought this would work just fine.
you are using the delete function wrong
Use string.delete(string[0]) instead of string.delete[string[0]]
EDIT
As for the infinite loop you mentioned.
Your condition for while is 0 < string.length
And you expect the string.delete[string[0]] statement to actually delete a character at a time.
But what exactly it does is, it deletes a character and returns the new string, but it never actually mutates/changes the actual string.
So try changing it to string = string.delete[string[0]]
Apart from using delete() instead of delete[] which has already been answered...
Most of what you need is implemented in Ruby's String class natively. each_char and count.
def most_common_letter(string)
max = [ nil, 0 ]
string.each_char {|char|
char_count = string.count(char)
max = [ char, char_count ] if char_count > max[1]
}
return max
end
You may do this in a much easier way, if you allow me to say.
def most_common_letter(string)
h = Hash.new
string.chars.sort.map { |c|
h[c] = 0 if (h[c].nil?)
h[c] = h[c] + 1
}
maxk = nil
maxv = -1
mk = h.keys
mk.each do |k|
if (h[k] > maxv) then
maxk = k
maxv = h[k]
end
end
[ maxk , maxv ]
end
If you test this with
puts most_common_letter("alcachofra")
the result will be
[ 'a', 3 ]
Finally, remember you don't need a return in the end of a Ruby method. The last value assigned is automatically returned.
Do Ruby in a Ruby way!

ruby returning true if a string consists an "a" and then a "z"

I need help on Writing a method that takes a string in and returns true if the letter "z" appears within three letters after an "a". You may assume that the string contains only lowercase letters. here's what I have:
def nearby_az(string)
string.downcase!
i = 0
while i < string.length
if (string[i] == "a" && string[i] == "z")
true
else
false
end
end
end
puts('nearby_az("baz") == true: ' + (nearby_az('baz') == true).to_s)
puts('nearby_az("abz") == true: ' + (nearby_az('abz') == true).to_s)
puts('nearby_az("abcz") == true: ' + (nearby_az('abcz') == true).to_s)
puts('nearby_az("a") == false: ' + (nearby_az('a') == false).to_s)
puts('nearby_az("z") == false: ' + (nearby_az('z') == false).to_s)
puts('nearby_az("za") == false: ' + (nearby_az('za') == false).to_s)
A regular expression would be the best for this. Try
def nearby_az(string)
(string =~ /a.{0,2}z/) != nil
end
EDIT:
as per the "if statement required requirement" :)
def nearby_az(string)
if (string =~ /a.{0,2}z/) != nil
return true
end
return false
end
The way this code works is it searches the input string for an "a". After that, the period indicates that you can have any character. After that you have {0,2} which is a modifier of the period indicating you can have 0 to 2 of any character. After this you must have a "z", and this fulfills your must have a z within 3 characters of an "a".
I've saved this regex to regex101 here so you can try various inputs as well as change the regular expression around to understand it better.
To fix you code you need to:
increment i at the end of the loop.
search the z letter within the 3 next letters
return true when condition is met
return false when getting out of the loop
Here is what it should look like:
def nearby_az(string)
string.downcase!
i = 0
while i < string.length do
return true if string[i] == "a" && string[i+1,3].include?(?z)
i+=1
end
return false
end

Why can't I find `"az"` in a string with this function?

I want to know if "a" and "z" are together in a string after I have found "a". I'd like to understand why this does not work:
def nearby_az(string)
i = 0
while i < string.length
if string[i] == "a" && string[i+1] == "z"
return true
else
return false
end
i += 1
end
end
I realize there is a simple way to implement this. I am not looking for another solution.
This code will only find "az" if it's at the very beginning. Otherwise it will return false. Postpone return false until you walked the whole string.
def nearby_az(string)
i = 0
while i < string.length -1
return true if string[i] == "a" && string[i+1] == "z"
i += 1
end
# we can only reach this line if the loop above does not return.
# if it doesn't, then the substring we seek is not in the input.
return false
end
nearby_az('baz') # => true
#Segio Tulentsev' s answer explains why yours is broken.
Here's the short implementation if you're interested
def nearby_az(str)
!! str =~ /a(?=z)/i
end

Find the first differing character between two Strings in Ruby

I have two strings.
str_a = "the_quick_brown_fox"
str_b = "the_quick_red_fox"
I want to find the first index at which the two strings differ (i.e. str_a[i] != str_b[i]).
I know I could solve this with something like the following:
def diff_char_index(str_a, str_b)
arr_a, arr_b = str_a.split(""), str_b.split("")
return -1 unless valid_string?(str_a) && valid_string?(str_b)
arr_a.each_index do |i|
return i unless arr_a[i] == arr_b[i]
end
end
def valid_string?(str)
return false unless str.is_a?(String)
return false unless str.size > 0
true
end
diff_char_index(str_a, str_b) # => 10
Is there a better way to do this?
Something like this ought to work:
str_a.each_char.with_index
.find_index {|char, idx| char != str_b[idx] } || str_a.size
Edit: It works: http://ideone.com/Ttwu1x
Edit 2: My original code returned nil if str_a was shorter than str_b. I've updated it to work correctly (it will return str_a.size, so if e.g. the last index in str_a is 3, it will return 4).
Here's another method that may strike some as slightly simpler:
(0...str_a.size).find {|i| str_a[i] != str_b[i] } || str_a.size
http://ideone.com/275cEU
i = 0
i += 1 while str_a[i] and str_a[i] == str_b[i]
i
str_a = "the_quick_brown_dog"
str_b = "the_quick_red_dog"
(0..(1.0)/0).find { |i| (str_a[i] != str_b[i]) || str_a[i].nil? }
#=> 10
str_a = "the_quick_brown_dog"
str_b = "the_quick_brown_dog"
(0..(1.0)/0).find { |i| (str_a[i] != str_b[i]) || str_a[i].nil? }
#=> 19
str_a.size
#=> 19
This uses a binary search to find the index where a slice of str_a no longer occurs at the beginning of str_b:
(0..str_a.length).bsearch { |i| str_b.rindex(str_a[0..i]) != 0 }

Resources