.start_with? method not recognizing substring 'a' - ruby

I want to translate a string into pig latin. The rules are as following:
Valid words are two or more letters long.
If a word begins with a consonant (a letter other than 'a', 'e', 'i', 'o', or 'u'), then that first letter is shifted to the end of the word.
Then add 'ay'.
I managed to come up with the method:
def translate(word)
if word.size <= 2
word
elsif
word.size > 2
!word.start_with?('a', 'e', 'i', 'o', 'u')
x = word.reverse.chop.reverse
x.insert(-1, word[0])
x << "ay"
else
word << "ay"
end
end
However, my test does not pass for certain strings,
Test Passed: Value == "c"
Test Passed: Value == "pklqfay"
Test Passed: Value == "yykay"
Test Passed: Value == "fqhzcbjay"
Test Passed: Value == "ndnrzzrhgtay"
Test Passed: Value == "dsvjray"
Test Passed: Value == "qnrgdfay"
Test Passed: Value == "npfay"
Test Passed: Value == "ldyuqpewypay"
Test Passed: Value == "arqokudmuxay"
Test Passed: Value == "spvhxay"
Test Passed: Value == "firvmanxay"
Expected: 'aeijezpbay' - Expected: "aeijezpbay", instead got: "eijezpbaay"
Expected: 'etafhuay' - Expected: "etafhuay", instead got: "tafhueay"
These tests passes:
Test.assert_equals(translate("billy"),"illybay","Expected: 'illybay'")
Test.assert_equals(translate("emily"),"emilyay","Expected: 'emilyay'")
I am not sure why.

If the length of word is greather or equal to 2 return word, if not then do the start_with step, but when would your else statement work?
Try modifying your length validation just to less than 2, then check if the word "start_with" a vowel, and return just the word plus ay, and if not the do the first character rotate step then adding the ay part, something like:
def translate(word)
if word.size < 2
word
elsif word.start_with?('a', 'e', 'i', 'o', 'u')
word << "ay"
else
x = word.reverse.chop.reverse
x.insert(-1, word[0])
x << "ay"
end
end

Your code fails because it doesn't really evaluates if the word begins with a constant, you are only checking it but doing nothing about it, its just an isolated line:
!word.start_with?('a', 'e', 'i', 'o', 'u')
Try including that line inside an if condition, like this:
def translate(word)
if word.size <= 2
word
else
if !word.start_with?('a', 'e', 'i', 'o', 'u')
x = word.reverse.chop.reverse
x.insert(-1, word[0])
x << "ay"
else
word << "ay"
end
end
end
Also notice that i removed the if word.size > 2, it is not necessary since you are already checking for word.size <= 2, so anything other than that is > 2.

Related

Question on how to filter x || y and not x && y

I am having trouble using || ("or").
This is the first time I select using the "or" feature and I have been trying to select the words that are greater than 6 characters long OR start with an "e". I tried everything but I keep getting just one feature or an "and". This is the code so far
def strange_words(words)
selected_words = []
i = 0
while i < words.length
word = words[i]
if word.length < 6
selected_words << word
end
i += 1
end
return selected_words
end
print strange_words(["taco", "eggs", "we", "eatihhg", "for", "dinner"])
puts
print strange_words(["keep", "coding"])
Using the || operator is the same as writing multiple if statements. Let's use a silly example to demonstrate it. Say you wanted to determine if a word started with the letter 'e'. Well there are a few forms of 'e'. There is the lowercase e and the upppercase E. You want to check for both forms so you could do something like this:
def starts_with_e?(string)
result = false
if string[0] == 'e'
result = true
end
if string[0] == 'E'
result = true
end
result
end
Notice however that you're doing the same actions after checking for the condition. This means you could simplify this code using the OR/|| operator, like such:
def starts_with_e?(string)
result = false
if string[0] == 'e' || string[0] == 'E'
result = true
end
end
For your specific question, you can do the following:
def strange_words(words)
words.select { |word| word.length < 6 || word[0] == 'e' }
end
When you run with your example, it gives you this output:
> strange_words(["taco", "eggs", "we", "eatihhg", "for", "dinner"])
=> ["taco", "eggs", "we", "eatihhg", "for"]
This is still not good code. You'll want to protect the methods from bad input.

Conditional statement not triggered

I wrote a piece of code that creates a random string based on an input. The user decides its length and whether it should contain numbers and special characters. I added a fail-safe routine because the end result is random:
def create
i = #number
while i != 0
# testing the script I have noticed that the if statement is (always) ignored.
if i == 2 && (#word_bank.include?(#special_chars) && #rand_ary.include?(#special_chars) == false)
#rand_ary << #special_chars[rand(0..#special_chars.size - 1)]
end
letter = rand(0..word_bank.size - 1)
#puts "#{i}, #{word_bank[letter]}"
#rand_ary << word_bank[letter]
word_bank.delete_at(letter)
i -= 1
end
#rand_string = #rand_ary.join()
puts #rand_string
#rand_string
end
If the user elects to include a special character, a counter runs from n - 0. When i = 2, and no character from a special character array is included, a random special character is manually included.
But this if-statement is never triggered. I can't figure out why.
If your variables have the kind of values I think they do, it's because include isn't the right method to use here.
If #word_bank and #rand_ary are arrays, include will check if any single element is equal to #special_chars. If #special_chars is itself an array, then it'll only return true if one of the elements in #word_bank/#rand_ary is an array.
['a', 'b', 'c', '!'].include?('!') # => true
['a', 'b', 'c', '!'].include?(['!']) # => false
['a', 'b', 'c', ['!']].include?(['!']) # => true
I think you're actually interested in whether there's any overlap between them. In that case, you can use the intersection (&) operator and check whether it's empty.
['a', 'b', 'c', '!'] & ['!'] # => ['!']
['a', 'b', 'c'] & ['!'] # => []

How to do string slicing in Ruby

This is a Pig Latin translate practice in Ruby.
Why am I getting different results from these two versions of code? In other words, why is word = word[i..-1] not taking effect in the second code block?
def translate(input)
output_array = input.split(" ").each do |word|
i=0
while !['a', 'e', 'i', 'o', 'u'].include?(word[i])
i += 1
end
unless i == 0
word << word[0..i-1]
word[0..i-1] = ''
end
word << "ay"
end
return output_array.join(" ")
end
puts translate('apple')
puts translate('banana')
puts translate('trash')
puts translate('eat pie')
which outputs:
appleay
ananabay
ashtray
eatay iepay
And:
def translate(input)
output_array = input.split(" ").each do |word|
i=0
while !['a', 'e', 'i', 'o', 'u'].include?(word[i])
i += 1
end
unless i == 0
word << word[0..i-1]
word = word[i..-1]
end
word << "ay"
end
return output_array.join(" ")
end
puts translate('apple')
puts translate('banana')
puts translate('trash')
puts translate('eat pie')
prints out:
appleay
bananab
trashtr
eatay piep
output_array = input.split(" ").each do |word|
i=0
while !['a', 'e', 'i', 'o', 'u'].include?(word[i])
i += 1
end
unless i == 0
word << word[0..i-1] # Good
word = word[i..-1] # Bad
end
word << "ay"
end
The line
word << word[0..i-1]
changes the string in place, while
word = word[i..-1]
creates a new string and assigns the new string to word. Changing the new string does not affect the old string in the array, so the words in the array stay what they were after
word << word[0..i-1]
Do every modification in-place (like what you did in solution 1), or use Array#map which is more Ruby-like.
This is off-topic, but your while loop can be replaced by
i = word.index(/[aeiou]/)
if you happen to know regular expressions.

Why does my ruby pig latin translator not capitalize the first word of a string properly?

I am trying to write a program that translates a string with some capitalized words and punctuation into Pig Latin. Here are the conditions:
1) words beginning with a vowel should just tack on "ay".
2) words beginning with a single phoneme like "sch" or "qu" or "squ" or "ch" should move all of those characters to the end, not just the first letter, and then tack on "ay".
3) the regular pig latin rules for a word beginning with one consonant (i.e., "Well," => 'Ellway,").
4) capitalization and punctuation should be preserved, but the initial letter would change if the letter doesn't begin with a vowel. So "Well," would become "Ellway,".
Everything works, except for the first word of my string. The fourth condition is never met with the first word of a string. So, for example, "Well," becomes "ellWay,". So punctuation works, but the capitalization isn't working properly.
Edit: I have realized that this issue occurs only when the word does NOT begin with a vowel. So, "Actually," becomes "Actuallyay," (which it should), but "Quaint," becomes "aintQuay,", when it should be "Aintquay,". So, here is the code where I actually pass the pig latin into the array named pig_latin:
string = string.split(' ')
pig_latin = []
string.each do |word|
if vowels.include?(word[0])
pig_latin << word + "ay"
elsif (consonants.include?(word[0]) && consonants.include?(word[1]) && consonants.include?(word[2])) || word[1..2].include?('qu')
pig_latin << (word[3..-1] + word[0..2] + "ay")
elsif (consonants.include?(word[0]) && consonants.include?(word[1])) || word[0..1].include?('qu')
pig_latin << (word[2..-1] + word[0..1] + "ay")
else
pig_latin << (word[1..-1] + word[0] + "ay")
end
end
Here is the part of my code that handles the capitalization and punctuation. To clarify, pig_latin is the array with the pig-latinized phrase passed into it. uppercase_alphabet is an array i created to include all uppercase letters:
idx1 = 0
while idx1 < pig_latin.count
word = pig_latin[idx1]
idx2 = 0
while idx2 < word.length
if uppercase_alphabet.include?(word[idx2])
word[idx2] = word[idx2].downcase
word[0] = word[0].upcase
end
if punctuation.include?(word[idx2])
word[word.length], word[idx2] = word[idx2], ''
end
idx2 += 1
end
idx1 += 1
end
pig_latin.join(' ')
Edit: Here is the code outlining the various arrays I'm using:
vowels = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']
lowercase_alphabet = ('a'..'z').to_a
uppercase_alphabet = ('A'..'Z').to_a
alphabet = lowercase_alphabet + uppercase_alphabet
punctuation = ['.', ',', ';', '?', '!', ':']
consonants = []
alphabet.each do |letter|
consonants << letter unless vowels.include?(letter)
end
And, here are the errors I'm getting when I run the test with the following string: "Well, I have, not even. seen that movie." (I understand the punctuation makes no sense).
1) #translate retains punctuation from the original phrase
Failure/Error: s.should == "Ellway, Iay avehay, otnay evenay. eensay atthay oviemay."
expected: "Ellway, Iay avehay, otnay evenay. eensay atthay oviemay."
got: "ellWay, Iay avehay, otnay evenay. eensay atthay oviemay." (using ==)
# ./spec/04_pig_latin_spec.rb:83:in `block (2 levels) in <top (required)>
It is hard to debug imaginary code. You are asking people why your code doesn't work without providing the values of some key variables.
Her are some tips:
1) A String works just like an Array, so you don't have to create an
Array of individual letters, which forces you to type all the commas and quote marks:
vowels = 'aeiou'
vowels.include?('a') #=>true
2) You don't have to include caps in your arrays of consonants and vowels, instead you can downcase before calling include?():
ch = 'A'
vowels.include?(ch.downcase) #=> true
3) When you are debugging, puts and p(for Arrays, Hashes) are your friend. You can find out which elsif branches are executing by adding puts/p statements:
if vowels.include?(word[0])
puts 'X'
pig_latin << word + "ay"
elsif (consonants.include?(word[0]) && consonants.include?(word[1]) && consonants.include?(word[2])) || word[1..2].include?('qu')
puts 'A'
pig_latin << (word[3..-1] + word[0..2] + "ay")
elsif (consonants.include?(word[0]) && consonants.include?(word[1])) || word[0..1].include?('qu')
puts 'B'
pig_latin << (word[2..-1] + word[0..1] + "ay")
else
puts 'C'
pig_latin << (word[1..-1] + word[0] + "ay")
end
end
4) When you are comparing strings, you can use ==. Instead of this:
word[0..1].include?('qu')
...you can write:
if word[0..1].downcase == 'qu'
It's more efficient to use == when you can.
5) Your if conditionals are too complex. If you know what regexes are, you can simplify things by extracting the consonants at the beginning of a word, and then using if statements to test what the consonants are:
words = %w{
schlepp
quail
squall
checkers
}
consonants = ('a'..'z').to_a.join.tr('aeiou', '')
words.each do |word|
md = word.match(/
\A #match start of string, followed by...
[#{consonants}]+ #a consonant, 1 or more times
/x)
if md
starting_consonants = md[0]
#Test for starting_consonants here, e.g.
#if starting_consonants == 'q' and word[1] == 'u'
# do something
else #then word starts with a vowel
...
end
end
--output:--
schl
q
sq
ch
You can limit the number of consonants extracted to three like this:
[#{consonants}]{1,3}
6) I would handle capitalization at the same time you change the words--then you won't have to search through all the letters in every word. First thing, check for capitalization of first letter (then set a flag variable, e.g. capitalized = true). Then downcase the first letter. Then after you change the word, if there was a capital, upcase the first letter(you can also call capitalize(), but the result can be different than just calling upcase() on the first letter). That way you don't have to search through the whole word in your complicated nested loop. Be sure to set the flag variable back to false.
7) In ruby, you rarely use while loops and increment a counter:
while idx2 < word.length
char = word[idx2]
...
...
idx2 += 1
end
Instead, you use each() loops:
word.each_char do |char|
#do something with char
end

Ruby -- If Elsif Else Error

I'm getting an error here with a simple if else chain, and I can't figure out what is going on. I started learning ruby the other day, I already know some java, and was just trying to re-write programs to learn ruby faster. I am trying to tally vowels and consonants. Anyways here is my code...
#!/usr/bin/ruby/
alphabet = 'abcdefghijklmnopqrstuvwxyz'
array = alphabet.chars.to_a
vowel = 0
cons = 0
puts array.at(1)
for i in 0...26
if array.at(i) == "a"
vowel++
elsif array.at(i) == 'e'
vowel++
elsif array.at(i) == 'i'
vowel++
elsif array.at(i) == 'o'
vowel++
elsif array.at(i) == 'u'
vowel++
else
cons++
end#end if else chain
end#end for loop
puts 'Vowel: ' + vowel.to_s
puts 'Consonants: ' + cons.to_s
Here is the error I am getting:
C:/Users/Kelan/Documents/Programming/Ruby
Files/Little Programs/Alphabet.rb:11:
syntax error, unexpected keyword_elsif
elsif array.at(i) == 'e'
^
C:/Users/Kelan/Documents/Programming/Ruby
Files/Little Programs/Alphabet.rb:13:
syntax error, unexpected keyword_elsif
elsif array.at(i) == 'i'
^
C:/Users/Kelan/Documents/Programming/Ruby
Files/Little Programs/Alphabet.rb:15:
syntax error, unexpected keyword_elsif
elsif array.at(i) == 'o'
^
C:/Users/Kelan/Documents/Programming/Ruby
Files/Little Programs/Alphabet.rb:17:
syntax error, unexpected keyword_elsif
elsif array.at(i) == 'u'
^
C:/Users/Kelan/Documents/Programming/Ruby
Files/Little Programs/Alphabet.rb:19:
syntax error, unexpected keyword_else
C:/Users/Kelan/Documents/Programming/Ruby
Files/Little Programs/Alphabet.rb:21:
syntax error, unexpected keyword_end
C:/Users/Kelan/Documents/Programming/Ruby
Files/Little Programs/Alphabet.rb:25:
syntax error, unexpected $end,
expecting keyword_end puts
'Consonants: ' + cons.to_s
^
[Finished in 0.203 seconds]
I'm sure it's just something silly, but I've been looking forever online for help and I have heard of your great community, so I thought I would try here,
Kelan
There is no ++ operator in Ruby. You should have used += 1
You may also want to learn about case statement:
alphabet = 'abcdefghijklmnopqrstuvwxyz'
26.times do |i|
case alphabet[i]
when 'a' then vowel += 1
when 'e' then vowel += 1
when 'i' then vowel += 1
when 'o' then vowel += 1
when 'u' then vowel += 1
else cons += 1
end#end case
end#end times
puts 'Vowel: ' + vowel.to_s
puts 'Consonants: ' + cons.to_s
Or, even better, use method count from class String, like this:
alphabet = 'abcdefghijklmnopqrstuvwxyz'
vowels = 'aeiou'
vowel_count = alphabet.count vowels
cons_count = alphabet.length - vowel_count
puts "Vowels: #{vowel_count}"
puts "Consonants: #{cons_count}"
Your problem is you're using the Java/PHP/C style increment operator. Ruby isn't down with that. You have to use foo += 1 instead.
How about I show you a more Ruby way of doing this though?
# use a range to define your alphabet
alphabet = ('a'..'z').entries #=> ['a', 'b', 'c', ...]
# define vowels as members of an array. it's more flexible, which
# is great for things that change (what if you decide to use 'y'?)
vowels = %w{ a e i o u } #=> ['a', 'e', 'i', 'o', 'u']
# keep counts all together in a hash, which I personally find cleaner
counts = { :vowels => 0, :consonants => 0 }
# even the `for` loops in ruby use the iterators, so you actually
# get better performance out of using the more user-friendly `.each`
alphabet.each do |letter|
if vowels.include? letter
counts[:vowels] += 1
else
counts[:consonants] += 1
end
end
puts "There were #{counts[:vowels]} vowels and #{counts[:consonants]} consonants."
I think rather than vowel++ and con++, you need to use vowel+=1 and con+=1.
Ruby does not have C-style pre/post incrementors.
Here is yet another way to write the demo:
puts("%d vowels & %d consonants" % ('a'..'z').inject([0,0]) do |m, e|
m[/[aeiou]/.match(e) ? 0:1] += 1; m
end)
There is an easy way of constructing the set of alphabets, using Range.
Since you are using ruby, you should use internal iterators instead of the external ones. You will rarely see a for loop in a good program in ruby.
case construction is handy in this case. You can put multiple matching patterns into one, separated by comma.
There is no ++ or -- operators in ruby. Use += 1.
Use "#{ }" notation. It's much better and faster than using +. In fact, you can omit to_s if you use it.
I would go like this:
vowel = 0
cons = 0
('a'..'z').each do |c|
case c
when 'a', 'e', 'i', 'o', 'u'; vowel += 1
else cons += 1
end
end
puts "Vowel: #{vowel}"
puts "Consonants: #{cons}"
If I wanted a shorter one, I might go with this:
partition = ('a'..'z').group_by{|c| c =~ /[aeiou]/}
puts "Vowel: #{partition[0].length}"
puts "Consonants: #{partition[nil].length}"

Resources