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

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

Related

Take in string, return true if after "a", a "z" appears within three places

# Write 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.
I came up with this, which seems logical, but for some reason if "z" comes directly after "a", it returns false. Can someone explain why?
def nearby_az(string)
i = 0
if string[i] == "a" && string[i+1] == "z"
return true
elsif string[i] == "a" && string[i+2] == "z"
return true
elsif string[i] == "a" && string[i+3] == "z"
return true
else return false
end
i += 1
end
#shivram has given the reason for your problem. Here are a couple of ways to do it.
Problem is tailor-made for a regular expression
r = /
a # match "a"
.{,2} # match any n characters where 0 <= n <= 2
z # match "z"
/x # extended/free-spacing regex definition mode
!!("wwwaeezdddddd" =~ r) #=> true
!!("wwwaeeezdddddd" =~ r) #=> false
You would normally see this regular expression written
/a.{0,2}z/
but extended mode allows you to document each of its elements. That's not important here but is useful when the regex is complex.
The Ruby trick !!
!! is used to convert truthy values (all but false and nil) to true and falsy values (false or nil) to false:
!!("wwwaeezdddddd" =~ r)
#=> !(!("wwwaeezdddddd" =~ r))
#=> !(!3)
#=> !false
#=> true
!!("wwwaeezdddddd" =~ r)
#=> !(!("wwwaeeezdddddd" =~ r))
#=> !(!nil)
#=> !true
#=> false
but !! is not really necessary, since
puts "hi" if 3 #=> "hi"
puts "hi" if nil #=>
Some don't like !!, arguing that
<condition> ? true : false
is more clear.
A non-regex solution
def z_within_4_of_a?(str)
(str.size-3).times.find { |i| str[i]=="a" && str[i+1,3].include?("z") } ? true : false
end
z_within_4_of_a?("wwwaeezdddddd")
#=> true
z_within_4_of_a?("wwwaeeezdddddd")
#=> false
This uses the methods Fixnum#times, Enumerable#find and String#include? (and String#size of course).
Your solution is incorrect. You are considering only the case where String starts with a (with i = 0 at the start of your method). I can see you are incrementing i at the end, but its of no use as its not in a loop.
I can think of a solution as to find the index of a in string, then take substring from that index + 3 and look for z. Something like:
s = "wwwaeezdddddd"
s[s.index("a")..s.index("a")+3]
#=> "aeez"
s[s.index("a")..s.index("a")+3] =~ /z/ # checking if z is present
#=> 3
If a can occur more than once in input String, you need to find all indices of a and run the above logic in a loop. Something like:
s = "wwwaesezddddddaz"
indexes = (0 ... s.length).find_all { |i| s[i,1] == 'a' }
#=> [3, 14]
indexes.each { |i| break if #is_present = s[i..i+3] =~ /z/ }
#is_present
#=> 1
Let’s implement the FSM ourselves :)
input = "wwwaeezdddddd"
!(0...input.length).each do |idx|
next unless input[idx] == 'a' # skip unrelated symbols
current = (idx..[idx + 3, input.length - 1].min).any? do |i|
input[i] == 'z' # return true if there is 'z'
end
# since `each` returns truthy (range itself),
# in case of success we return falsey and negate
break false if current
end
#⇒ true
Please note, that the above implementation is O(length(input)) and does not use any built-in ruby helpers, it is just iterating a string char by char.
While the regexp solution is the most elegant, here is one for completion, which is more in spirit to your original attempt:
def nearby_az(string)
!!(apos = string.index('a') and string[apos,3].index('z'))
end

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

Ruby code for deleting the vowels in a string

I'm trying to write code in Ruby that removes all the vowels from a string:
def remvowel(string)
i = 0
dv_string = []
while i < string.length
if (string[i] != "a" || string[i] != "e" || string[i] != "i" || string[i] != "o" || string[i] != "u")
dv_string.push(i)
i += 1
end
i += 1
end
return dv_string.join
end
But it's not coming out right. When I run remvowel("duck"), it returns "02", as in the index positions of "dc". I'm missing something, but I don't know what.
You could just:
string.gsub(/[aeiou]/, '')
Or even better:
string.tr('aeiou', '')
And the best tool for deleting characters in a string is...
string.delete('aeiou')
That's because you're pushing i instead of string[i].
dv_string.push(i)
This is what you want:
dv_string.push(string[i])
However, that's a rather verbose and roundabout way of accomplishing the task. A somewhat more idiomatic Ruby approach would look like any of the ones ndn posted:
def remvowel(string)
string.gsub /[aeiou]/, ''
end
or
def remvowel(string)
string.tr 'aeiou',''
end
or
def remvowel(string)
string.delete 'aeiou'
end
You've got it almost right:
def remvowel(string)
i = 0
dv_string = []
while i < string.length
if (string[i] != "a" || string[i] != "e" || string[i] != "i" || string[i] != "o" || string[i] != "u")
# Push the letter, not i
dv_string.push(string[i])
# Don't increment i here
end
i += 1
end
return dv_string.join
end
Your algorithm increments i twice if you encounter a consonant, so you are skipping every second letter.
Here is one more way this can be done:
s = "Hello, how are you you?"
vowels = "aeiou"
puts (s.chars - vowels.chars).join
#=> Hll, hw r y y?
Thank you everybody for your contributions. Thanks to you all (and especially you, Cary Swoveland), I not only know better ways to do this in the future, but even found an answer for my "scenic route" way of doing it!
def remvowel(string)
i = 0
dv_string = []
while i < string.length
dv_string.push(string[i])
if (string[i] == "a" || string[i] == "e" || string[i] == "i" || string[i] == "o" || string[i] == "u")
dv_string.delete(string[i])
end
i += 1
end
return dv_string.join
end
Granted, I'm gonna be doing the more sensible way from the responses here from now on, but mission accomplished!

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!

How do I count vowels?

I've seen the solution and it more or less matches
Write a method that takes a string and returns the number of vowels
in the string. You may assume that all the letters are lower cased. You can treat "y" as a consonant.
Difficulty: easy.
def count_vowels(string)
vowel = 0
i = 0
while i < string.length
if (string[i]=="a" || string[i]=="e" || string[i]=="i" || string[i]=="o"|| string[i]=="u")
vowel +=1
end
i +=1
return vowel
end
puts("count_vowels(\"abcd\") == 1: #{count_vowels("abcd") == 1}")
puts("count_vowels(\"color\") == 2: #{count_vowels("color") == 2}")
puts("count_vowels(\"colour\") == 3: #{count_vowels("colour") == 3}")
puts("count_vowels(\"cecilia\") == 4: #{count_vowels("cecilia") == 4}")
def count_vowels(str)
str.scan(/[aeoui]/).count
end
/[aeoui]/ is a regular expression that basically means "Any of these characters: a, e, o, u, i". The String#scan method returns all matches of a regular expression in the string.
def count_vowels(str)
str.count("aeoui")
end
Your function is fine you are just missing a keyword end to close of your while loop
def count_vowels(string)
vowel = 0
i = 0
while i < string.length
if (string[i]=="a" || string[i]=="e" || string[i]=="i" || string[i]=="o"|| string[i]=="u")
vowel +=1
end
i +=1
end
return vowel
end
puts("count_vowels(\"abcd\") == 1: #{count_vowels("abcd") == 1}")
puts("count_vowels(\"color\") == 2: #{count_vowels("color") == 2}")
puts("count_vowels(\"colour\") == 3: #{count_vowels("colour") == 3}")
puts("count_vowels(\"cecilia\") == 4: #{count_vowels("cecilia") == 4}")
#=> count_vowels("abcd") == 1: true
#=> count_vowels("color") == 2: true
#=> count_vowels("colour") == 3: true
#=> count_vowels("cecilia") == 4: true
I think using HashTable data structure would be good way to go for this particular problem. Especially if you're required to output number of every single vowel separately.
Here is the code I'd use:
def vowels(string)
found_vowels = Hash.new(0)
string.split("").each do |char|
case char.downcase
when 'a'
found_vowels['a']+=1
when 'e'
found_vowels['e']+=1
when 'i'
found_vowels['i']+=1
when 'o'
found_vowels['o']+=1
when 'u'
found_vowels['u']+=1
end
end
found_vowels
end
p vowels("aeiou")
Or even this (elegant but not necessarily performant):
def elegant_vowels(string)
found_vowels = Hash.new(0)
string.split("").each do |char|
case char.downcase
when ->(n) { ['a','e','i','o','u'].include?(n) }
found_vowels[char]+=1
end
end
found_vowels
end
p elegant_vowels("aeiou")
which would output:
{"a"=>1, "e"=>1, "i"=>1, "o"=>1, "u"=>1}
So you don't have to turn the string into an array and worry about case sensitivity:
def getVowelCount(string)
string.downcase.count 'aeiou'
end

Resources