Ruby: Reverse method from scratch - ruby

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

Related

if statement doesn't get executed in Ruby

I'm new to Ruby programming language and i am asked to make a small program that does the following:
Rule 1: If a word begins with a vowel sound, add an "ay" sound to the end of the word.
Rule 2: If a word begins with a consonant sound, move it to the end of the word, and then add an "ay" sound to the end of the word.
but in my if else statement it doesn't go into the if even if its true it stays at the else statement
i have tried taking the string and converting it into an array and work on the array and tried working on the string as is
def translate (str)
i = 0
while i < str.length
if (str[i] == "a" or str[i] == "e" or str[i] == "o" or str[i] == "u" or str[i] == "i")
str = str + "ay"
return str
else
temp = str[0...1]
str = str[1...str.length]
str = str + temp
end
i = i + 1
end
end
s = translate("banana")
puts s
the program doesn't enter the if statement at all and keeps getting into the else statement until the word returns the same with out any changes
Aside from my suggestion to use || instead of or, your method doesn't need a #while iterator since you're checking only for the first letter. The if/else statement should be executed only once.
You can also replace all the checks with a single #include? method like this:
def translate (str)
if %w[a e i o u].include?(str[0])
str + "ay"
else
str[1..-1] + str[0] + "ay"
end
end
Notice that I've also removed the return statement since the last executed line will be returned, so either line 3 or line 5 in the method above.
You can also add a ternary operator to make it in one line:
%w(a e i o u).include?(str[0]) ? str + "ay" : str[1..-1] + str[0] + "ay"
Rule 1: If a word begins with a vowel sound, add an "ay" sound to the
end of the word.
translate("ana")
# ~> "anaay"
Rule 2: If a word begins with a consonant sound, move it to the end of
the word, and then add an "ay" sound to the end of the word.
translate("banana")
# ~> "ananabay"
If I understand the problem correctly, you do not need to loop at all here. You just need to check the first letter, and not all of the letters.
def translate (str)
if str[0] == 'a' or str[0] == 'e' or str[0] == 'o' or str[0] == 'u' or str[0] == 'i'
str + 'ay'
else
temp = str[0...1]
str = str[1...str.length]
str = str + temp
str + 'ay'
end
end
By the way, I was able to figure this out with the debugger. Did you try that at all? Also, with your original code it turns out that for some inputs (like 'baaan'), your else statement does execute.
I don't see a problem with or or || in this case.
The problem I see is that if the start letter is a consonant, you changing str rotating it's letters at each iteration (see the commented part of the code), so the starting letter is never a vowel.
Then you are missing a returning value at the end so it returns nil and puts nothing.
def translate (str)
i = 0
while i < str.length
p str[i] # it's never a vowel
if (str[i] == "a" or str[i] == "e" or str[i] == "o" or str[i] == "u" or str[i] == "i")
str = str + "ay"
return str
else # here you are rotating the word
temp = str[0...1]
str = str[1...str.length]
str = str + temp
p str
end
i = i + 1
end
# missing a return value
end
s = translate("banana")
p s
So it prints out:
# "b"
# "ananab"
# "n"
# "nanaba"
# "n"
# "anaban"
# "b"
# "nabana"
# "n"
# "abanan"
# "n"
# "banana"
# nil
The code works correctly in case the first letter is a vowel, so it enters the if true:
s = translate("ananas")
p s
#=> "ananasay"
By the way, as already posted by others, you don't need any while loop. Just checking the first letter with an if statement is enough.

Why one solution prints true when the other prints false?

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

How should I refactor this conditional code in ruby?

How to refactor this function?
def split_description(first_n)
description_lines = description.split "\n"
line_num = description_lines.length
if line_num > first_n
#description_first = to_html(description_lines[0..first_n].join("\n"))
#description_remain = to_html(description_lines[first_n + 1..line_num].join("\n"))
elsif line_num > 1
#description_first = to_html(description_lines[0..first_n].join("\n"))
#description_remain = ''
else
#description_first = ''
#description_remain = ''
end
end
I am a Ruby starter and encounter this rubocup warning: Method has too many lines. [13/10]
The following is whole code url:
https://github.com/RubyStarts3/YPBT-app/blob/master/views_objects/video_info_view.rb
Code
def split_description(description, first_n)
#description_first, #description_remain =
case description.count("\n")
when 0..first_n
[description, '']
else
partition_description(description, first_n)
end.map(&:to_html)
end
def partition_description(description, first_n)
return ['', description] if first_n.zero?
offset = 0
description.each_line.with_index(1) do |s,i|
offset += s.size
return [description[0,offset], description[offset..-1]] if i == first_n
end
end
I've assumed to_html('') #=> '', but if that's not the case the modification is straightforward.
Example
So that we can see the effect of to_html, let's define it thusly.
def to_html(description)
description.upcase
end
description =<<_
It was the best of times
it was the worst of times
it was the age of wisdom
it was the age of fools
_
split_description(description, 0)
#description_first
#=> ""
#description_remain
#=> "IT WAS THE BEST OF TIMES\n..WORST OF TIMES\n..AGE OF WISDOM\n..AGE OF FOOLS\n"
split_description(description, 1)
#description_first
#=> "IT WAS THE BEST OF TIMES\n"
#description_remain
#=> "IT WAS THE WORST OF TIMES\n..AGE OF WISDOM\n..AGE OF FOOLS\n"
split_description(description, 2)
#description_first
#=> "IT WAS THE BEST OF TIMES\nIT WAS THE WORST OF TIMES\n"
#description_remain
#=> "IT WAS THE AGE OF WISDOM\nIT WAS THE AGE OF FOOLS\n"
split_description(description, 3)
#description_first
#=> "IT WAS THE BEST OF TIMES\n..WORST OF TIMES\n..AGE OF WISDOM\n"
#description_remain
#=> "IT WAS THE AGE OF FOOLS\n"
split_description(description, 4)
#description_first
#=> "IT WAS THE BEST OF TIMES\n..WORST OF TIMES\n..AGE OF WISDOM\n..AGE OF FOOLS\n"
#description_remain
#=> ""
Explanation
Firstly, is appears that description is a local variable holding a string. If so, it must be an argument of the method (along with first_n).
def split_description(description, first_n)
We want to assign values to two instance variables, so let's begin by writing
#description_first, #description_remain =
There are really two steps: obtaining the desired strings and then mapping them with to_html. So let's first concentrate on the first step.
We will now condition on the number of lines in the string
case description.count("\n")
First, let's deal with the case where the string contains no newlines
when 0
[description, '']
If the string is empty this will be ['', '']; otherwise it will contain a single string without a newline.
Next, suppose the number of newlines in the string is between 1 and first_n. In this case #description_first is to be the entire string and #description_remain is to be empty.
when 1..first_n
[description, '']
As both when 0 and when 1..first_n return the same two-element array, we can combine them:
when 0..first_n
[description, '']
To get this far, first_n is less than the number of newlines. I've used another method for the case where the number of newlines is greater than first_n.
else
partition_description(description, first_n)
partition_description simply determines the offset into description of the first_nth newline, and then partitions the string accordingly.
Lastly, we need to end the case statement, map the array of two strings returned with to_html and end the method
end.map(&:to_html)
end
As I mentioned earlier, I've assumed to_html('') #=> ''. That seems to me to be the best place do deal with empty strings.
Note that I've dealt with the string directly, rather than splitting the string into lines, manipulating the lines and then rejoining them.
Since it's used or blanked in every condition, initialize the instance variables to blank.
def split_description(first_n)
description_lines = description.split "\n"
line_num = description_lines.length
#description_first = ''
#description_remain = ''
if line_num > first_n
#description_first = to_html(description_lines[0..first_n].join("\n"))
#description_remain = to_html(description_lines[first_n + 1..line_num].join("\n"))
elsif line_num > 1
#description_first = to_html(description_lines[0..first_n].join("\n"))
end
end
I'd also move the logic for description_lines[first_n + 1..line_num].join("\n") to a method like to_html( whatever_that_is( lines, from, to) ) or the like. Then it's not so bad if you repeat the same call and the name will describe what it's doing.
If first_n is always greater than 1 I think you can modify a little the Schwern's answer:
...
#description_first = to_html(description_lines[0..first_n].join("\n")) if line_num > 1
if line_num > first_n
#description_remain = to_html(description_lines[first_n + 1..line_num].join("\n"))
end
end
This should work :
def split_description(description, first_n = 0)
lines = description.each_line
#description_first = to_html(lines.take(first_n).join)
#description_remain = to_html(lines.drop(first_n).join)
end
take and drop replace all your logic, because, as #Cary Swoveland mentionned in a comment :
if you take too much, you end up with the complete array, without error message
if you drop too much, you end ud with an empty array, without error message
Example :
[1,2].take(99) #=> [1, 2]
[1,2].drop(99) #=> []
Also each_line outputs an Array of Strings, with newlines still present. No split, chomp or join("\n") is needed.

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

What's wrong with this Ruby program that counts vowels?

When I test this program on strings the output is 0. I think my logic is sound and it's just a minor syntax thing. Anyone see the problem?
def VowelCount(string)
string.downcase
i = 0
vowels = 0
until i == string.length-1
if (string[i] == "a" || string[i] == "o" || string[i] == "e" || string[i] == "i" || string[i] == "u")
vowels += 1
end
i += 1
end
return vowels
end
You can use String#count:
str = "It was the best of times, it was the worst of times,..."
str.downcase.count('aeiou') #=> 14
The following line
until i == string.length-1
should be:
until i == string.length
Otherwise, the last character is not checked.
BTW, by convension, method name starts with lower case, and combined with underscore. Here's an alternative solution using regular expression.
def vowel_count(string)
string.scan(/[aeiou]/i).length
end
update
As JesseSielaff pointed, String#downcase does not change the string in place. You need to assign the return value of the method back or use String.downcase!

Resources