Ruby: returning modified string after iteration - ruby

So ive been trying to write the pig latin method with its different instances. However, the join method at the end simply joins the original words of the string as opposed to the modified words that have gone through the iteration:
def translate(string)
alphabet = ("a".."z").to_a
vowels = %w{a e i o u}
consonant = alphabet - vowels
words = string.split
words.each do |word|
if vowels.include?(word[0])
word = word + "ay"
elsif word[0..2] == "sch"
word = word[3..-1] + word[0..2] + "ay"
elsif word[0..1] == "qu"
word = word[2..-1] + word[0..1] + "ay"
elsif word[1..2] == "qu"
word = word[3..-1] + word[0..2] + "ay"
elsif consonant.include?(word[0]) && consonant.include?(word[1]) && consonant.include?(word[2])
word = "#{word[3..-1]}#{word[0..2]}ay"
elsif consonant.include?(word[0]) && consonant.include?(word[1])
word = "#{word[2..-1]}#{word[0..1]}ay"
elsif consonant.include?(word[0])
word = "#{word[1..-1]}#{word[0]}ay"
end
end
p words.join(" ")
end
translate("apple pie")
translate("cherry")
translate("school")
translate("square")
translate("three")
translate("apple")
translate("cat")
This is what it gives me when it runs:
"apple pie"
"cherry"
"school"
"square"
"three"
"apple"
"cat"

Try using map instead of each
Like so:
def translate(string)
alphabet = ("a".."z").to_a
vowels = %w{a e i o u}
consonant = alphabet - vowels
words = string.split
result = words.map do |word|
if vowels.include?(word[0])
word = word + "ay"
elsif word[0..2] == "sch"
word = word[3..-1] + word[0..2] + "ay"
elsif word[0..1] == "qu"
word = word[2..-1] + word[0..1] + "ay"
elsif word[1..2] == "qu"
word = word[3..-1] + word[0..2] + "ay"
elsif consonant.include?(word[0]) && consonant.include?(word[1]) && consonant.include?(word[2])
word = "#{word[3..-1]}#{word[0..2]}ay"
elsif consonant.include?(word[0]) && consonant.include?(word[1])
word = "#{word[2..-1]}#{word[0..1]}ay"
elsif consonant.include?(word[0])
word = "#{word[1..-1]}#{word[0]}ay"
end
word
end
p result.join(" ")
end
translate("apple pie")
translate("cherry")
translate("school")
translate("square")
translate("three")
translate("apple")
translate("cat")
I suggest reading this answer Array#each vs. Array#map
because it will shed some light on the difference between map and each. Your each block is returning the original words array, so that is why it was never changed.

Using regular expressions and a case statement, you can compact it quite a bit:
def pig_latin(word)
case word
when /^[aeiou]/ #starts with a vowel
word + "ay"
when /^([s?qu|[^aeiou]{1,3})/ #starts with qu, squ or 1-3 consonants
rep = $1
word.sub(rep, "") + "#{rep}ay"
end
end
def translate(string)
string.split.map { |word| pig_latin(word) }.join " "
end
resulting in the following tests being all true:
puts translate("apple pie") == "appleay iepay"
puts translate("cherry") == "errychay"
puts translate("school") == "oolschay"
puts translate("square") == "aresquay"
puts translate("three") == "eethray"
puts translate("apple") == "appleay"
puts translate("cat") == "atcay"
Note that I simplified the second regular expression with the assumption that the only consonant that can precede "qu" at the beginning of a word is "s" (which is the case according to /usr/share/dict/words).

Related

ruby access non-vowels of a string in condition line without arrays

I need to turn a string that contains (among other letters) a sequence of 3 letters consisting of a non-vowel, an "o" and the same non-vowel again
into
a string that contains (other letters and) only that non-vowel.
like
"kok" #=> "k"
"mom" #=> "m"
"lol" #=> "l"
"kokmomloljk" #=> "kmljk"
I would like my code to be as compact as possible and only use string methods.
str.each_char { | i | if i == /[^aeiou]/ and i == str[i.index + 2] and str[i.index + 1] == "o"
str = str.delete(str.slice(str[i.index + 1], 2))
end
}
The output is the unchanged string. Thank you in advance.
R = /
([^aeiou]) # match a consonant in capture group 1
o # match an 'o'
\1 # match the contents of capture group 1
/x # free-spacing regex definition mode
def my_method(str)
str.gsub(R,'\1')
end
my_method "my dog kok has fleas"
#=> "my dog k has fleas"
my_method "much momentum"
#=> "much mentum"
my_method "'plolly' is not a word"
#=> "'plly' is not a word"
my_method "abkokcmomloljkde"
#=> "abkcmljkde"
my_method "bub"
#=> "bub"
I was wondering if you could do this in a more functional non-destructieve way with map and, yes you can, but not more compact than the other answers:
str = "iskakmomlol"
VOWEL = /[aeiou]/
RESONANT = /[^aeiou]/
str.chars.map.with_index { |c, i|
prevb, prev, nxt, scnd = str[i - 2], str[i - 1], str[i + 1], str[i + 2]
if i > str.length - 1 || i == 0 then c
elsif c =~ RESONANT && nxt =~ VOWEL && c == scnd then c
elsif c =~ VOWEL && prev =~ RESONANT && nxt =~ RESONANT
elsif c =~ RESONANT && prevb == c && prev =~ VOWEL
else c
end
}.compact.join # "iskmljk"
Actually, this can be shorter:
R = /([^aeiou])[aeiou]\1/
str.chars.map.with_index { |c, i|
c unless str[i-1..i+1][R] || str[i-2..i][R]
}.compact.join # "iskmljk"
i figured since the "o" is a fix character in the sequence to be accepted, i can just go through with a count var and see if the characters next to it are equal to each other and non-vowels. also i found that slice can be also passed two parameters so that it slices starting at an offset and stopping after the given length.
index = 0
while index < str.length
index = index + 1
if str[index] == "o" and str[index-1] == str[index+1] and str[index-1] != /[^aeiou]/
str.slice!(index, 2)

Difference between two codes: I get false for one and true for another

I got these two codes from two websites defining the longest word in a string:
Code 1.
def longest_word(sentence)
words = sentence.split(" ")
longest_word = nil
word_idx = 0
while word_idx < words.length
current_word = words[word_idx]
if longest_word == nil
longest_word = current_word
elsif longest_word.length < current_word.length
longest_word = current_word
end
word_idx += 1
end
longest_word
end
Code 2.
def LongestWord(sen)
arr = sen.split.map do |w|
/[a-zA-Z0-9\s]+/.match(w)
end
longest = arr.max_by do |w|
w.to_s.length
end
longest
end
But one code gives me true, and one gives me false. Can someone tell me why?
The difference is:
LongestWord returns MatchData http://ruby-doc.org/core-2.2.0/MatchData.html
longest_word returns String
so
longest_word(sentence).eql?(LongestWord(sentence).to_s) == true

Pig-latin translator parsing "qu" erratically

I'm working on the pig_latin_spec example from The Odin Project and having some difficulty handling special cases in which a word contains "qu" and those characters need to be treated as one phoneme. For example "quiet" => "ietquay" and "square" => "aresquay".
Here's the offending if statement:
if word.include? "qu"
start_index = word.index("q")
prefix = word[0..(start_index+1)] + "ay"
pig_word = word[(start_index+2)..-1] + prefix
end
This produces "ietquay" but not "aresquay". Instead I get "uaresqay". What am I overlooking?
Here's the entire program:
def translate(input)
all_words = []
pig_word = ""
input.split.map do |word, i|
if 'aeiou'.include? word[0]
pig_word = word + "ay"
else
prefix = word[0] + "ay"
pig_word = word[1..-1] + prefix
if word.include? "qu"
start_index = word.index("q")
prefix = word[0..(start_index+1)] + "ay"
pig_word = word[(start_index+2)..-1] + prefix
end
unless 'aeiou'.include? word[1]
prefix = word[0..1] + "ay"
pig_word = word[2..-1] + prefix
unless 'aeiou'.include? word[2]
prefix = word[0..2] + "ay"
pig_word = word[3..-1] + prefix
end
end
end
all_words << pig_word
end
"#{[all_words].join(" ")}"
end
Your special-casing for "qu" is being run, but then replaced by the logic in the unless with this condition:
'aeiou'.include? word[1]
because the condition is false for word = "square" (because "q" is not in "aeiou").
A quick patch/fix is to use if...elsif to make the two competing conditional blocks mutually exclusive, and when the word contains "qu" it will not check further.
if word.include? "qu"
start_index = word.index("q")
prefix = word[0..(start_index+1)] + "ay"
pig_word = word[(start_index+2)..-1] + prefix
elsif ! 'aeiou'.include? word[1]
prefix = word[0..1] + "ay"
pig_word = word[2..-1] + prefix
unless 'aeiou'.include? word[2]
prefix = word[0..2] + "ay"
pig_word = word[3..-1] + prefix
end
end
Note this may not lead to a perfect translator, it just fixes the issue you have brought here as a question.
Implementing Neil's suggested edit, here is the fully-functional code:
def translate(input)
all_words = []
pig_word = ""
input.split.map do |word, i|
first_vowel = word.index(/[aeiou]/) #dat first vowel occurence
if first_vowel == 0 #words that begin with a vowel
pig_word = word + "ay"
elsif word.include? "qu" #words that don't begin with a vowel but include "qu".
first_vowel = word.index(/[aeio]/) #first vowel not counting "u"
start_index = word.index("q")
prefix = word[0..(first_vowel-1)] + "ay"
pig_word = word[first_vowel..-1] + prefix
end
if first_vowel >= 1 #words that don't begin with vowels or have "qu"
prefix = word[0..first_vowel-1] + "ay"
pig_word = word[first_vowel..-1] + prefix
end
all_words << pig_word
end
"#{[all_words].join(" ")}"
end

pig latin ruby translator issue

Hello I'm having trouble with my pig latin code, I'm working on the Learn First files from Ruby, what I basically wanted to do was the following, if a word began with a vowel=> apple would become appleay, if it began with a consonant => banana => ananabay, if it began with two consonants => cherry => errychay, it can also translate two words => eat pie would become eatay iepay and so on and so forth, here is my code:
def translate(string)
vowels = [ "a" , "e" , "i" , "o" , "u"]
alphabet = ("a" .. "z").to_a
consonants = alphabet - vowels
string_split = string.split
string_split.map! do |w|
if vowels.include?(w[0])
w + 'ay'
elsif consonants.include?(w[0]) &&
consonants.include?(w[1])
w [2..-1] + w [0..1]+ 'ay'
elsif w [0..1] == "qu"
w[2..-1] + "quay"
elsif w[0..2] == "thr"
w [3..-1]+"thray"
elsif w[0..2]== "sch"
w[3..-1]+"schay"
elsif consonants.include?(w[0])
w[ 1..-1] + w[0..0] + 'ay'
else
w
end
end
return string_split.join(" ")
end
very simple, it's elsif not elseif
def translate(string)
vowels = [ "a" , "e" , "i" , "o" , "u"]
alphabet = ("a" .. "z").to_a
consonants = alphabet - vowels
string_split = string.split
string_split.map! do |w|
if vowels.include?(w[0])
w + 'ay'
elsif consonants.include?(w[0]) &&
consonants.include?(w[1])
w [2..-1] + w [0..1]+ 'ay'
elsif w [0..1] == "qu"
w[2..-1] + "quay"
elsif w[0..2] == "thr"
w [3..-1]+"thray"
elsif w[0..2]== "sch"
w[3..-1]+"schay"
else consonants.include?(w[0])
w[ 1..-1] + w[0..0] + 'ay'
end
end
return string_split.join(" ")
end

unexpected end kend ruby piglatin program

I needed to write a program that converts normal speech into pig latin. I wrote
def translate(*string)
word = []
word_string = []
s = []
i = 0
a = nil
#enters input from user into individual arrays spots
#removes spaces
string[0].scan(/\w+/).each { |x| word << x }
#goes through each word and does operation
while i < word.count
word[i].scan(/./) { |x| word_string << x }
#checks if starts with vowel
if word_string[0].include?("aeiou" || "AEIOU")
word_string = word_string << "ay"
s[i] << word_string.join('')
#checks if starts with Qu or qu
elsif word_string[0] + word_string[1] == "qu" || word_string[0] + word_string[1] == "Qu"
word_string.delete_at(0) && word_string.delete_at(1)
word_string << "quay"
s[i] = word_string.join('')
#checks if starts with 3 consonants
unless (word_string[0] + word_string[1] + word_string[2]).include?("aeiou")
a = word_string[0] + word_string[1] + word_string[2]
word_string.delete_at(0) && word_string.delete_at(1) && word_string.delete_at(2)
word_string << (a + "ay")
s[i] = word_string.join('')
a = nil
#checks if starts with 2 consonants
unless (word_string[0] + word_string[1]).include?("aeiou")
a = word_string[0] + word_string[1]
word_string.delete_at(0) && word_string.delete_at(1)
word_string << (a + "ay")
s[i] = word_string.join('')
a = nil
#check if starts with 1 consonants
else
a = word_string[0]
word_string.delete_at(0)
word_string << (a + "ay")
s[i] = word_string.join('')
a = nil
end
i += 1
end
s.join(" ")
end
It returned to me a error saying
pig_latin.rb:58: syntax error, unexpected $end, expecting kEND
I looked into the error, it means that I either missed a end somewhere or I have one too many, but I am unable to find it. I have a def end, a while end and a if end so the issue isn't there. I thought it might be somewhere in the first few linkes where I wrote the scans to sort the text originally but it doesn't seem like its there either. I need another pair of eyes to take a look, I can't find it. Also if there would be a better way to write this, please let me know.
This is more how the code should look, if it was written in more of a Ruby way:
def translate(string)
pig_latin = []
words = string.split(/\W+/)
words.each do |word|
case word
when /^[aeiou]/i
pig_latin << (word + "ay")
when /^qu/i
word << word[0,2] << 'ay'
pig_latin << word[2 .. -1]
when /^[^aeiou]{3}/i
word << word[0,3] << 'ay'
pig_latin << word[3..-1]
when /^[^aeiou]{2}/i
word << word[0, 2] << 'ay'
pig_latin << word[2 .. -1]
else
word << word[0] << 'ay'
pig_latin << word[1 .. -1]
end
end
pig_latin.join(' ')
end
puts translate('the rain in spain stays mainly on the plain')
=> ethay ainray inay ainspay aysstay ainlymay onay ethay ainplay
I'd have checked for consonants differently.
How it works is left to the reader to figure out. If this was a homework assignment, take the time to figure out how it works, because knowing what it does is important. Copying other people's work... well, this is on the internet now so anyone can search and find it, so don't plagiarize.

Resources