I could not get the intended output from the written code - ruby

I have to write a method called consonant_cancel that takes in a sentence and returns a new sentence where every word begins with its first vowel. The intended output is for given test functions are:
puts consonant_cancel("down the rabbit hole") #=> "own e abbit ole"
puts consonant_cancel("writing code is challenging") #=> "iting ode is allenging"
But I am getting "own e abbit it ole e" and "iting ing ode e is allenging enging ing" with this code.
def consonant_cancel(sentence)
arr = []
vowels = 'aeiou'
words = sentence.split
words.each do |word|
word.each_char.with_index do |char, i|
if vowels.include?(char)
arr << word[i..-1]
end
end
end
return arr.join(' ')
end
puts consonant_cancel("down the rabbit hole") #=> "own e abbit ole"
puts consonant_cancel("writing code is challenging") #=> "iting ode is allenging"
Can you guys help me to debug it?

You can use String#gsub with a regular expression. There is no need to break the string into pieces for processing and subsequent recombining.
def consonant_cancel(str)
str.gsub(/(?<![a-z])[a-z&&[^aeiou]]+/i,'')
end
consonant_cancel("down the rabbit hole")
#=> "own e abbit ole"
consonant_cancel("writing code is challenging")
#=> "iting ode is allenging"
See the section "Character Classes" in the doc for Regexp for an explanation of the && operator.
We can write the regular expression in free-spacing mode1 to make it self-documenting.
/
(?<! # Begin a negative lookbehind
[a-z] # Match a lowercase letter
) # End negative lookbehind
[a-z&&[^aeiou]]+ # Match one or more lowercase letters other than vowels
/ix # Invoke case-indifference and free-spacing modes
The negative lookahead ensures that no string of letters immediately preceded by a letter is matched. The line
[a-z&&[^aeiou]]+
can alternatively be written
[b-df-hj-np-tv-z]+
1. See the section "Free-Spacing Mode and Comments" in the doc for Regexp.

If we adding in a puts to see what's happening in your loop:
def consonant_cancel(sentence)
arr = []
vowels = 'aeiou'
words = sentence.split
words.each do |word|
word.each_char.with_index do |char, i|
if vowels.include?(char)
puts char
arr << word[i..-1]
end
end
end
return arr.join(' ')
end
Then running consonant_cancel "hello world" we see:
irb(main):044:0> consonant_cancel "hello world"
e
o
o
=> "ello o orld"
irb(main):045:0>
You'll see the same issue with any word with multiple consonants, because of the way you're looping over the characters in a word and checking for consonants.
An easier way to accomplish this would be with regular expressions.
words.split.map { |w| w.sub(/^[^aeiou]*/i, "") }.join(' ')

word.each_char.with_index
This loop iterates all chars (and vowels) of the word. break it after the first vowel found, so it does not repeat the side-effects for subsequent vowels of this word.
As an alternative, here's another regex-based solution
def consonant_cancel(sentence)
sentence.scan(/\b[^aeiou]*(.+?)\b/i).join(" ")
end

Related

How to skip over whitespaces in .map. Ruby

This is my code:
def weirdcase (string)
string.chars.map.with_index { |letter, index|
unless index.odd?;
letter = letter.upcase
else
letter
end }.compact.join("")
end
This is what it's supposed to do:
"ThIs Is A TeSt"
And this is what I got:
"ThIs iS A TeSt"
It's giving me the wrong string in return because it's counting/including the white spaces in my
code. All I need to do is find a way to skip the white spaces then I'm good to go.
Thanks!
The problem
I assume that the objective is to capitalize, for each word, all letters at even indices (the first letter of the word having index zero).
Here are two ways to do that. Both methods use String#gsub with a regular expression. Depending on requirements it may be necessary to change str.gsub... to str.downcase.gsub... for both methods.
Use a regular expression to match one- or two-characters strings, two if possible, and capitalize those strings.
def weirdcase(str)
str.gsub(/(?<=\A| |[^ ]{2})[^ ]{1,2}/) { |s| s.capitalize }
end
weirdcase "this is a sentence for testing"
#=> "ThIs Is A SeNtEnCe FoR TeStInG"
The regular expression reads, "match one or two characters other than spaces, two if possible ([^ ]{1,2}), that are immediately preceded by one of the following: the beginning of the string (\A), a space or two characters other than spaces. (?<=\A| |[^ ]{2}) is a positive lookbehind.
s.capitalize invokes the method String#capitalize on the match.
Use a cycling enumerator
def weirdcase(str)
enum = [:upcase, :downcase].cycle
str.gsub(/./) do |s|
if s == ' '
enum.rewind
' '
else
s.public_send(enum.next)
end
end
end
weirdcase "this is a sentence for testing"
#=> "ThIs Is A SeNtEnCe FoR TeStInG"
The regular expression /./ matches each character in the string.
See Array#cycle, Enumerator#rewind, Enumerator#next and Object#public_send.
Note the following.
enum = [:upcase, :downcase].cycle
#=> #<Enumerator: [:upcase, :downcase]:cycle>
enum.next
#=> :upcase
enum.next
#=> :downcase
enum.next
#=> :upcase
enum.rewind
#=> #<Enumerator: [:upcase, :downcase]:cycle>
enum.next
#=> :upcase
enum.next
#=> :downcase
... ad infinitum

How to I get the right output when there is more than vowel in each word? My code only works with one vowel in each word

Aba is a German children’s game where secret messages are exchanged. In Aba,
after every vowel we add “b” and add that same vowel.
Write a method aba_translate that takes in a sentence string and returns a new
sentence representing its Aba translation. Capitalized words of the original sentence
should be properly capitalized in the new sentence.
aba_translate(“Cats and dogs”) #=> “Cabats aband dobogs”
aba_translate(“Everyone can code”) #=> “Ebeveryobonebe caban cobodebe”
aba_translate(“Africa is Africa in German”) #=> “Abafribicaba ibis Abafribicaba ibin
Gebermaban”
My code:
def aba_translate(sentence)
translation = []
words = sentence.split(" ")
vowels = "aeiou"
vowel = ""
before = ""
after = ""
full = ""
words.each do |word|
word.each_char.with_index do |char, idx|
if vowels.include?(char)
vowel = char
before = word[0...idx]
after = word[idx+1..-1]
full = before + vowel + "b" + vowel + after
translation << full
end
end
end
return translation.join(" ")
end
puts aba_translate("Cats and dogs")
puts aba_translate("Everyone can code")
puts aba_translate("Africa is Africa in German")
Your code generates a whole new word every time it sees a vowel. Instead you need to build each word character by character and make changes when it sees a vowel.
def aba_translate(sentence)
translation = []
words = sentence.split(" ")
vowels = "aeiouAEIOU"
words.each do |word|
full = ""
word.each_char.with_index do |char, idx|
full += char
if vowels.include?(char)
full = full + "b" + char.downcase
end
end
translation << full
end
return translation.join(" ")
end
Every time you find a vowel, you take the entire string before the vowel and the entire string after the vowel and add it to the result.
So, for a word like "code", that means you first produce the output c + obo + de and then the output cod + ebe.
However, what you actually need to do is simply keep the part you have processed instead of duplicating it.
You can do this by either changing your logic to keep track of up to which index you have already processed the word, or alternatively by processing it character-by-character instead of chunk-by-chunk.
However for problems like this, Regex are usually a much better solution:
VOWELS = 'aeiou'
def aba_translate(sentence)
sentence.gsub(Regexp.union(*VOWELS.chars), '\0b\0')
end
or just making VOWELS a Regexp in the first place:
VOWELS = /[aeiou]/.freeze
def aba_translate(sentence)
sentence.gsub(VOWELS, '\0b\0')
end
I think the nested loops complicates it since you can solve the problem with just one loop. Here you just need to initialize a string and a string of vowels to check each character. While you iterate through each character you shovel it into the empty string, and then you check if it is a vowel you shovel b + that vowel's lowercase version to account for the uppercase instances. Then you finally return the new string.
def aba_translate(string)
new_string = ""
vowels = "AEIOUaeiou"
string.each_char do |char|
new_string << char
if vowels.include?(char)
new_string << "b" + char.downcase
end
end
return new_string
end
Here's my beginner-friendly solution
def aba_translate(sent)
vowels = "AEIOUaeiou"
aba_sent = ""
sent.each_char do |char|
if vowels.include?(char)
aba_sent += char + "b" + char.downcase
else
aba_sent += char
end
end
return aba_sent
end

Ruby method that uppercases even indexed letters and lowercases odd

Directions:
Write a method that accepts a string, and returns the same string with all even indexed characters in each word upper cased, and all odd indexed characters in each word lower cased. The indexing just explained is zero based, so the zero-ith index is even, therefore that character should be upper cased.
The passed in string will only consist of alphabetical characters and spaces(' '). Spaces will only be present if there are multiple words. Words will be separated by a single space(' ').
My code:
(someone please refactor or explain to me a cleaner/shorter solution)
def weirdcase(string)
arr = string.split(' ')
arr.map! {|word|
char = word.chars
char.each_with_index do |letter, i|
i % 2 == 0 ? letter.upcase! : letter.downcase!
end
}
arr.map! {|a| a.push(' ').join('')}
x = arr.join('').to_s
x[0...-1]
end
This is one way you could do that, using Array#cycle to create an enumerator and String#gsub to replace every character in the string with its value upcased or downcased.
def weirdcase(str)
enum = [:upcase, :downcase].cycle
str.gsub(/./) do |s|
if s == ' '
enum.rewind
s
else
s.public_send(enum.next)
end
end
end
weirdcase "Mary had a little lamb"
#=> "MaRy hAd a lItTlE LaMb"
By making gsub's argument /./ each character in the string is replaced by the value returned by the block, which, if that character is not a space, is that character either upcased or downcased, depending on the symbol generated by the enumerator enum, which alternates between :upcase and :downcase for each word.
Note that
enum = [:upcase, :downcase].cycle
#=> #<Enumerator: [:upcase, :downcase]:cycle>
enum.next
#=> :upcase
enum.next
#=> :downcase
enum.next
#=> :upcase
and so on. See also Enumerator#next.
Enumerator#rewind is needed to begin anew the alternating of case with each word.
One could replace s.public_send(enum.next) with
enum.next == :upcase ? s.upcase : s.downcase
You could also use gsub to change two adjacent characters at a time:
def weirdcase(string)
string.gsub(/(.)(.?)/) { "#{$1.upcase}#{$2.downcase}" }
end
weirdcase "Mary had a little lamb"
#=> "MaRy hAd a lItTlE LaMb"
The ? makes the second character optional, which is needed for odd-length strings:
weirdcase "foo"
#=> "FoO"
Or a using each_char and with_index:
def weirdcase(string)
string.each_char.map.with_index { |char, index|
if index.odd?
char.downcase
else
char.upcase
end
}.join
end
If you want to change each word separately:
"Mary had a little lamb".split(' ').map { |word| weirdcase(word) }.join(' ')
#=> "MaRy HaD A LiTtLe LaMb"
or again with gsub:
"Mary had a little lamb".gsub(/\S+/) { |word| weirdcase(word) }
#=> "MaRy HaD A LiTtLe LaMb"

Pig Latin converter

I'm currently working on an exercise which involves converting the sentence "The quick brown fox" to “Hetay uickqay rownbay oxfay”
def translate(sent)
sent = sent.downcase
vowels = ['a', 'e', 'i', 'o', 'u']
words = sent.split(' ')
result = []
words.each_with_index do |word, i|
translation = ''
qu = false
if vowels.include? word[0]
translation = word + 'ay'
result.push(translation)
else
word = word.split('')
count = 0
word.each_with_index do |char, index|
if vowels.include? char
# handle words that start with 'qu'
if char == 'u' and translation[-1] == 'q'
qu = true
translation = words[i][count..words[i].length] + translation + 'ay'
result.push(translation)
next
end
break
else
# handle words with 'qu' in middle
if char == 'q' and translation[i-1] == 'u'
qu = true
translation = words[i][count +1..words[i].length] + 'ay'
result.push(translation)
next
else
translation += char
end
count += 1
end
end
# translation of consonant words without "qu"
if not qu
translation = words[i][count..words[i].length] + translation + 'ay'
result.push(translation)
end
end
end
result.join(' ')
end
puts translate("The quick brown fox")
However, I'm getting "ethay uickqay ownbray oxfay" instead of “Hetay uickqay rownbay oxfay”.
Where are the areas that needs correction? I couldn't pinpoint the problem. Could you show me the solution?
This is a very procedural and complex way of going about this. I'm not sure what your rules for checking q and u are for, since the pig latin translation rules make no mention of qu as a special case:
A far simpler way is to split the sentence into an array of words, and transform each word as required:
def translate(sent)
translation = sent.split(' ').map do |w|
vowels = %w(a e i o u)
word = w.downcase.split('')
if vowels.include? word[0]
"#{word}way"
else
"%s%say" % [word[1..-1], word[0]]
end
end
translation.join(' ').capitalize
end
puts translate("The quick brown fox")
# outputs Hetay uickqay rownbay oxfay
And, for 1.9 and likely above:
def translate(sent)
translation = sent.split(' ').map do |w|
vowels = %w(a e i o u)
word = w.downcase.split('')
if vowels.include? word[0]
"#{word.join}way"
else
"%s%say" % [word[1..-1].join, word[0]]
end
end
translation.join(' ').capitalize
end
puts translate("The quick brown fox")
Obviously, both of these are examples and can likely be made far better with work. But they serve to illustrate the point.
This makes use of map and join, and can probably be optimised further. The key difference between this method and yours is that you are attempting to build up a map of the translation iteratively, when you probably do not need to. Use the enumeration functions, they are part of what makes the functional programming style more expressive. Learn to think "how do I permute this data set to get my desired response" as opposed to "What steps do I need to perform to get my desired response".

How can I check for first letter(s) in string in Ruby?

I'm writing test file, but I can't get it pass second test, here:
def translate(word)
if word.start_with?('a','e','i','o','u')
word << "ay"
else
word << "bay"
end
end
Is start_with? the right method to do the job?
describe "#translate" do
it "translates a word beginning with a vowel" do
s = translate("apple")
s.should == "appleay"
end
it "translates a word beginning with a consonant" do
s = translate("banana")
s.should == "ananabay"
end
it "translates a word beginning with two consonants" do
s = translate("cherry")
s.should == "errychay"
end
end
EDIT:
My solution is not complete.
My code pass first test only because I was able to push "ay" to the end of word. What I'm missing to pass the second test is to remove the first letter if its consonant, which is "b" in "banana".
You can do this also:
word << %w(a e i o u).include?(word[0]) ? 'ay' : 'bay'
Using a Regex might be overkill in your case, but could be handy if you want to match more complex strings.
word << word[0].match(/a|e|i|o|u/).nil? ? 'bay' : 'ay'
Your code means:
if word start with ('a','e','i','o','u') add "ay" at the end
else add "bay" at the end.
Second test will be "bananabay" and not "ananabay" (with b as first letter)
def translate(word)
prefix = word[0, %w(a e i o u).map{|vowel| "#{word}aeiou".index(vowel)}.min]
"#{word[prefix.length..-1]}#{prefix}ay"
end
puts translate("apple") #=> "appleay"
puts translate("banana") #=> "ananabay"
puts translate("cherry") #=> "errychay"
Looks like you are removing the first character if the word starts with a consonant too, so:
if word.start_with?('a','e','i','o','u')
word[0] = ''
word << 'ay'
else
consonant = word[0]
word << "#{consonant}ay"
end
The below piece of code passes all the tests...
def translate(word)
if word.start_with?('a','e','i','o','u')
word<<'ay'
else
pos=nil
['a','e','i','o','u'].each do |vowel|
pos = word.index(vowel)
break unless pos.nil?
end
unless pos.nil?
pre = word.partition(word[pos,1]).first
word.slice!(pre)
word<<pre+'ay'
else
#code to be executed when no vowels are there in the word
#eg words fry,dry
end
end
end
Figured I'd share my first contribution!
Good luck!
def method(word)
word[0].eql?("A" || "E" || "I" || "O" || "U")
end

Resources