0

I got an exercise to replace any vowel from a string with an X. The following returns an error. I am not sure why. I don't want to use String#replace.

def disemvowel (string)
  vowels_caps = [A, E, I, O, U]
  vowels_notcaps = [a, e, i, o, u]

  stringarray = string.split("")

  stringarray.each do |letter|
    if vowels_caps.include? letter or vowels_notcaps.include? letter
        letter = "X" 
    end
  end

  string = stringarray.join("")

  string
end

disemvowel(abcdefghijklmnopqrstuvwxyz)
sawa
  • 165,429
  • 45
  • 277
  • 381

3 Answers3

8

Excellent choice of method name.

You have a few problems; let's take them one at a time. First:

undefined local variable or method `abcdefghijklmnopqrstuvwxyz'

When you write strings, you need to wrap them in '' or "". Ruby is trying to interpret abcdefghijklmnopqrstuvwxyz as a variable like letter or stringarray. So that should look like:

disemvowel('abcdefghijklmnopqrstuvwxyz')

Next, uninitialized constant A. This is the same problem, happening when Ruby tries to evaluate vowel_caps and vowel_notcaps:

vowels_caps = ['A', 'E', 'I', 'O', 'U']

Creating arrays of strings is common, so there's a shorthand using %w that easily creates an array of words, separated by whitespace:

vowels_caps = %w[A E I O U]
vowels_notcaps = %w[a e i o u]

Now you run the script, but seemingly nothing happens. Make sure you print your output:

puts disemvowel('abcdefghijklmnopqrstuvwxyz')

Great, so there's some output, but it's just the alphabet again. The failure here is that you expect letter = "X" to replace the letter inside stringarray, but it does not. letter is its own variable, independent of stringarray - it's not a pointer into the array itself. There are a few ways to fix this, but I think the clearest might be to switch from each to map, since you're already trying to join the result afterward. Where each returns the original array, map returns an array containing the result of each iteration.

resultarray = stringarray.map do |letter|
  if vowels_caps.include? letter or vowels_notcaps.include? letter
    "X"
  else
    letter
  end
end
puts resultarray.inspect

I've just put that print there temporarily to verify that indeed the X is subbed in for each vowel:

["X", "b", "c", "d", "X", "f", "g", "h", "X", "j", "k", "l", "m", "n", "X", "p", "q", "r", "s", "t", "X", "v", "w", "x", "y", "z"]

As a side note—and this can start something of a war occasionally—you should prefer || over or in boolean logic. The operators have different precedence and using or this way can surprise you.

if vowels_caps.include?(letter) || vowels_notcaps.include?(letter)

Now just join it back up like you have been, and there's actually no need to reassign it to string. Ruby returns the value of the last expression in the function, which can just be:

resultarray.join("")

And voila!

$ ruby disemvowel.rb
XbcdXfghXjklmnXpqrstXvwxyz
Kristján
  • 18,165
  • 5
  • 50
  • 62
2

It should be as simple as this. Use the String#tr

'abcdefghijklmnopqrstuvwxyz'.tr('aeiouAEIOU', 'X')
#=> "XbcdXfghXjklmnXpqrstXvwxyz"

'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.tr('aeiouAEIOU', 'X')
#=> "XBCDXFGHXJKLMNXPQRSTXVWXYZXbcdXfghXjklmnXpqrstXvwxyz"
Babar Al-Amin
  • 3,939
  • 1
  • 16
  • 20
2
str = "abcdefghijklmnopqrstuvwxyz"
str.gsub(/[aeiou]/i, 'x')
# => "xbcdxfghxjklmnxpqrstxvwxyz"
Rubysmith
  • 1,165
  • 8
  • 12
  • 2
    While this is a working solution to the exercise, it doesn't answer the question why the code above doesn't work. You can skip the `downcase` by making your regexp case-insensitive (`/[aeiou]/i`). – undur_gongor Mar 08 '16 at 12:24