I'm having trouble with a Ruby pig latin translator translating 2 or more words. I've successfully figured out how to translate words beginning with a vowel, consonant, or two consonants with my function translate, and I'm wanting to create a second function, translate_words, that uses .map with the first function.
When the string, "eat pie", gets submitted, the output is "eat pieay". It only changes the second word and also does it incorrectly (should be "eatay iepay"). I've looked at multiple other solutions on SO without luck. I'm still very new with regex so those solutions were a little over my head.
This project works with RSpec and I've added the test code below mine.
Here's my code:
def translate(input)
pig_string = ''
if input[0] =~ /[aeiou]/
return input + 'ay'
elsif input[0] =~ /[^aeiou]/ && input[1] =~ /[aeiou]/
return input[1..-1] + input[0] + 'ay'
elsif input[0..1] =~ /[^aeiou]/
return input[2..-1] + input[0..1] + 'ay'
else
return input[0] + input + 'ay'
end
end
def translate_words(multi_words)
word_count = multi_words.split.size
if word_count > 1
multi_words.map! do |word|
translate(word)
end
end
end
RSpec:
it "translates two words" do
s = translate("eat pie")
expect(s).to eq("eatay iepay")
end
As per comments above by bitsapien and Sergio Tulentsev :
def translate_words(multi_words)
multi_words.split.map do |word|
translate(word)
end.join(' ')
end
map will suffice instead of map!.
translate_words('eat plie') #=> "eatay ieplay"
Related
I have been tasked to create a pig_latin method.
Pig Latin is a made-up children's language that's intended to be
confusing. It obeys a few simple rules (below) but when it's spoken
quickly it's really difficult for non-children (and non-native
speakers) to understand.
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.
(There are a few more rules for edge cases, and there are regional
variants too, but that should be enough to understand the tests.)
All my tests are passing save one, to translate many words.
This is my error:
#translate
translates a word beginning with a vowel
translates a word beginning with a consonant
translates a word beginning with two consonants
translates two words
translates a word beginning with three consonants
counts 'sch' as a single phoneme
counts 'qu' as a single phoneme
counts 'qu' as a consonant even when it's preceded by a consonant
translates many words (FAILED - 1)
Failures:
1) #translate translates many words
Failure/Error: expect(s).to eq("ethay ickquay ownbray oxfay")
expected: "ethay ickquay ownbray oxfay"
got: "ethay"
(compared using ==)
# ./spec/04_pig_latin_spec.rb:70:in `block (2 levels) in <top (required)>'
Finished in 0.00236 seconds (files took 0.10848 seconds to load)
9 examples, 1 failure
Failed examples:
rspec ./spec/04_pig_latin_spec.rb:68 # #translate translates many words
And this is my method:
def translate(str)
def add_ay(str)
return str + 'ay'
end
def word_begins_with_vowel(str)
if (!(str.match(' '))) && $vowels[str[0]]
return add_ay(str)
end
end
def begins_with_consonant(str)
if ((!$vowels[str[0]]) && (!$vowels[str[1]]) && (!$vowels[str[2]]))
first_three = str.split('').slice(0, 3).join('');
str = str.slice(3, str.length - 1)
return str + first_three + 'ay'
end
if ((!$vowels[str[0]]) && (!$vowels[str[1]]))
first_two = str.split('').slice(0, 2).join('');
str = str.slice(2, str.length - 1)
return str + first_two + 'ay'
end
if ((!$vowels[str[0]]))
first_char = str.split('').slice(0);
str = str.slice(1, str.length - 1)
return str + first_char +'ay'
end
end
def translates_two_words(str)
if (str.match(' '))
str = str.split(' ');
first_char = str[1].split('').slice(0);
str[1] = str[1].slice!(1, str[1].length - 1);
return str[0] + 'ay' + ' ' + str[1] + first_char + 'ay'
end
end
def translates_many_words(str)
str = str.split(' ');
if str.length > 2
str.each do |item|
return begins_with_consonant(item) || word_begins_with_vowel(item)
end
end
end
$vowels = {
'a' => add_ay(str),
'e' => add_ay(str),
'i' => add_ay(str),
'o' => add_ay(str),
'y' => add_ay(str)
}
return translates_many_words(str) || word_begins_with_vowel(str) || begins_with_consonant(str) || translates_two_words(str)
end
I would figure this would take care of many words:
def translates_many_words(str)
str = str.split(' ');
if str.length > 2
str.each do |item|
return begins_with_consonant(item) || word_begins_with_vowel(item)
end
end
end
but it's not.
As #theTinMan says, return - will decline next iteration and just return the first value in the first iteration, from my comment, I think, this should work for you(with minimum editing of your code):
def translates_many_words(str)
str = str.split(' ');
if str.length > 2
str.map do |item|
begins_with_consonant(item) || word_begins_with_vowel(item)
end.join(' ')
end
end
UPD
Also, I'll recommend you to refactor your code to make it more readable, it could help you in the future.
My variant of this method is:
def translates_many_words(str)
str = str.split
# line under - is a shortcut from `return nil if str.size <= 2`
# `#size` is more relative to this context if you will count elements of array
return unless str.size > 2
# Now, when we excluded possibility of work with array that have less then 2 elements,
# we can continue with our iteration
str.map do |item|
begins_with_consonant(item) || word_begins_with_vowel(item)
end.join(' ')
end
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".
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
Hi I'm trying to write code for to convert strings to pig latin
def translate(str)
alpha = ('a'..'z').to_a
vowels = %w[a e i o u]
consonants = alpha - vowels
if vowels.include?(str[0])
str + 'ay'
elsif str[0..1] == 'qu'
str[2..-1]+'quay'
elsif consonants.include?(str[0]) && str[1..2]=='qu'
str[3..-1]+str[0..2]+'ay'
elsif consonants.include?(str[0]) && consonants.include?(str[1]) && consonants.include?(str[2])
str[3..-1] + str[0..2] + 'ay'
elsif consonants.include?(str[0]) && consonants.include?(str[1])
str[2..-1] + str[0..1] + 'ay'
elsif consonants.include?(str[0])
str[1..-1] + str[0] + 'ay'
elsif str[0..1] == 'qu'
str[2..-1]+'quay'
else
return str
end
end
This code works perfect for converting one word strings, for example: translate("monkey").
What i'm trying to do is make it possible for this code to accept multiple words as well (within the same string)...following the above criteria for converting into pig latin, example:
translate("please help") => "easeplay elphay"
thanks much!
Since you already know how to translate a single word why not just split up the task into two methods:
def translate(str)
str.split.map { |word| translate_word(word) }.join
end
def translate_word(str)
# Your old translate code here
end
What I would do for this is:
use the #split method to make your str variable into an array of words (or 1 word if its only 1 word).
afterwards you can use the array#each method to iterate through each array index.
i.e.
str = "hello"
str = str.split(" ") # str now equals ["hello"]
for multiple variables:
str = "hello world"
str- str.split(" ") #now equals ["hello", "world"]
then you can use the .each method:
str.each do |<variable name you want to use>|
<how you want to manipulate the array>
end
for the pig latin program you could do:
str.each do|element|
if vowels.include?(element)
<do whatever you want here>
elsif
<do whatever>
else
<do whatver>
end
end
this will iterate through each element in the array and translate it (if there is only one element it will still work)
Trying to write Method in ruby that will translate a string in pig-latin , the rule :
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 and also when the word begins with 2 consonants , move both to the end of the word and add an "ay"
As a newbie , my prob is the second rule , when the word begin with only one consonant it work , but for more than one , I have trouble to make it work ,Can somebody look at the code and let me know how i can code that differently and probably what is my mistake , probably the code need refactoring. Thanks , so far i come up with this code :
def translate (str)
str1="aeiou"
str2=(/\A[aeiou]/)
vowel = str1.scan(/\w/)
alpha =('a'..'z').to_a
con = (alpha - vowel).join
word = str.scan(/\w/)
if #first rule
str =~ str2
str + "ay"
elsif # second rule
str != str2
s = str.slice!(/^./)
str + s + "ay"
elsif
word[0.1]=~(/\A[con]/)
s = str.slice!(/^../)
str + s + "ay"
else
word[0..2]=~(/\A[con]/)
s = str.slice!(/^.../)
str + s + "ay"
end
end
translate("apple") should == "appleay"
translate("cherry") should == "errychay"
translate("three") should == "eethray"
No need for all those fancy regexes. Keep it simple.
def translate str
alpha = ('a'..'z').to_a
vowels = %w[a e i o u]
consonants = alpha - vowels
if vowels.include?(str[0])
str + 'ay'
elsif consonants.include?(str[0]) && consonants.include?(str[1])
str[2..-1] + str[0..1] + 'ay'
elsif consonants.include?(str[0])
str[1..-1] + str[0] + 'ay'
else
str # return unchanged
end
end
translate 'apple' # => "appleay"
translate 'cherry' # => "errychay"
translate 'dog' # => "ogday"
This will handle multiple words, punctuation, and words like 'queer' = 'eerquay' and 'school' = 'oolschay'.
def translate (sent)
vowels = %w{a e i o u}
sent.gsub(/(\A|\s)\w+/) do |str|
str.strip!
while not vowels.include? str[0] or (str[0] == 'u' and str[-1] == 'q')
str += str[0]
str = str[1..-1]
end
str = ' ' + str + 'ay'
end.strip
end
okay this is an epic pig latin translator that I'm sure could use a bit of refactoring, but passes the tests
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 + 1..words[i].length] + translation + 'uay'
result.push(translation)
next
end
break
else
# handle words with 'qu' in middle
if char == 'q' and word[i+1] == 'u'
qu = true
translation = words[i][count + 2..words[i].length] + 'quay'
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
So this will give the following:
puts translate('apple') # "appleay"
puts translate("quiet") # "ietquay"
puts translate("square") # "aresquay"
puts translate("the quick brown fox") # "ethay ickquay ownbray oxfay"
def translate(sentence)
sentence.split(" ").map do |word|
word = word.gsub("qu", " ")
word.gsub!(/^([^aeiou]*)(.*)/,'\2\1ay')
word = word.gsub(" ", "qu")
end
end
That was fun! I don't like the hack for qu, but I couldn't find a nice way to do that.
So for this pig latin clearly I skipped and\an\in and singular things like a\I etc. I know that wasn't the main question but you can just leave out that logic if it's not for your use case. Also this goes for triple consonants if you want to keep it with one or two consonants then change the expression from {1,3} to {1,2}
All pig latin is similar so just alter for your use case. This is a good opportunity to use MatchData objects. Also vowel?(first_letter=word[0].downcase) is a style choice made to be more literate so I don't have to remember that word[0] is the first letter.
My answer is originally based off of Sergio Tulentsev's answer in this thread.
def to_pig_latin(sentence)
sentence.gsub('.','').split(' ').collect do |word|
translate word
end.compact.join(' ')
end
def translate(word)
if word.length > 1
if word == 'and' || word == 'an' || word == 'in'
word
elsif capture = consonant_expression.match(word)
capture.post_match.to_s + capture.to_s + 'ay'
elsif vowel?(first_letter=word[0].downcase)
word + 'ay'
elsif vowel?(last_letter=word[-1].downcase)
move_last_letter(word) + 'ay'
end
else
word
end
end
# Move last letter to beginning of word
def move_last_letter(word)
word[-1] + word[0..-2]
end
private
def consonant_expression
# at the beginning of a String
# capture anything not a vowel (consonants)
# capture 1, 2 or 3 occurences
# ignore case and whitespace
/^ [^aeiou] {1,3}/ix
end
def vowel?(letter)
vowels.include?(letter)
end
def vowels
%w[a e i o u]
end
Also just for the heck of it I'll include my dump from a pry session so you all can see how to use MatchData. MINSWAN. It's stuff like this that makes ruby great.
pry > def consonant_expression
pry * /^ [^aeiou] {1,3}/ix
pry * end
=> :consonant_expression
pry > consonant_expression.match('Stream')
=> #<MatchData "Str">
pry > capture = _
=> #<MatchData "Str">
pry > ls capture
MatchData#methods:
== begin end hash length offset pre_match regexp string to_s
[] captures eql? inspect names post_match pretty_print size to_a values_at
pry >
pry > capture.post_match
=> "eam"
pry > capture
=> #<MatchData "Str">
pry > capture.to_s
=> "Str"
pry > capture.post_match.to_s
=> "eam"
pry > capture.post_match.to_s + capture.to_s + 'ay'
=> "eamStray"
pry >
If I understood your question correctly, you can just directly check if a character is a vowel or consonant and then use array ranges to get the part of the string you want.
vowels = ['a', 'e', 'i', 'o', 'u']
consonants = ('a'..'z').to_a - vowels
return str + "ay" if vowels.include?(str[0])
if consonants.include?(str[0])
return str[2..-1] + str[0..1] + "ay" if consonants.include?(str[1])
return str[1..-1] + str[0] + "ay"
end
str
Here's a solution that handles the "qu" phoneme as well as other irregular characters. Had a little trouble putting the individual words back into a string with the proper spacing. Would appreciate any feedback!
def translate(str)
vowels = ["a", "e", "i", "o", "u"]
new_word = ""
str.split.each do |word|
vowel_idx = 0
if vowels.include? word[0]
vowel_idx = 0
elsif word.include? "qu"
until word[vowel_idx-2]+word[vowel_idx-1] == "qu"
vowel_idx += 1
end
else
until vowels.include? word[vowel_idx]
vowel_idx += 1
end
end
idx_right = vowel_idx
while idx_right < word.length
new_word += word[idx_right]
idx_right += 1
end
idx_left = 0
while idx_left < vowel_idx
new_word += word[idx_left]
idx_left += 1
end
new_word += "ay "
end
new_word.chomp(" ")
end
I done gone did one too
def translate(string)
vowels = %w{a e i o u}
phrase = string.split(" ")
phrase.map! do |word|
letters = word.split("")
find_vowel = letters.index do |letter|
vowels.include?(letter)
end
#turn "square" into "aresquay"
if letters[find_vowel] == "u"
find_vowel += 1
end
letters.rotate!(find_vowel)
letters.push("ay")
letters.join
end
return phrase.join(" ")
end
def piglatinize(word)
vowels = %w{a e i o u}
word.each_char do |chr|
index = word.index(chr)
if index != 0 && vowels.include?(chr.downcase)
consonants = word.slice!(0..index-1)
return word + consonants + "ay"
elsif index == 0 && vowels.include?(chr.downcase)
return word + "ay"
end
end
end
def to_pig_latin(sentence)
sentence.split(" ").collect { |word| piglatinize(word) }.join(" ")
end
This seems to handle all that I've thrown at it including the 'qu' phoneme rule...
def translate str
letters = ('a'..'z').to_a
vowels = %w[a e i o u]
consonants = letters - vowels
str2 = str.gsub(/\w+/) do|word|
if vowels.include?(word.downcase[0])
word+'ay'
elsif (word.include? 'qu')
idx = word.index(/[aeio]/)
word = word[idx, word.length-idx] + word[0,idx]+ 'ay'
else
idx = word.index(/[aeiou]/)
word = word[idx, word.length-idx] + word[0,idx]+'ay'
end
end
end
I'm grabbing the words with the 'qu' phoneme and then checking all the other vowels [excluding u].
Then I split the word by the index of the first vowel (or vowel without 'u' for the 'qu' cases) and dropping the word part before that index to the back of the word. And adding 'ay' ftw.
Many of the examples here are fairly long. Here's some relatively short code I came up with. It handles all cases including the "qu" problem! Feedback always appreciated (I'm pretty new to coding).
$vowels = "aeiou"
#First, I define a method that handle's a word starting with a consonant
def consonant(s)
n = 0
while n < s.length
if $vowels.include?(s[n]) && s[n-1..n] != "qu"
return "#{s[n..-1]}#{s[0..n-1]}ay"
else
n += 1
end
end
end
#Then, I write the main translate method that decides how to approach the word.
def translate(s)
s.split.map{ |s| $vowels.include?(s[0]) ? "#{s}ay" : consonant(s) }.join(" ")
end