wannabeprogrammer
wannabeprogrammer

Reputation: 35

Trying to find vowels of a string using Ruby while loops

def count_vowels(string)
  vowels = ["a", "e", "i", "o", "u"]
  i = 0
  j = 0
  count = 0

  while i < string.length do
    while j < vowels.length do
      if string[i] == vowels[j]
        count += 1
        break
      end

      j += 1
    end

    i += 1
  end

  puts count 
end

I'm having trouble spotting where this goes wrong. If this program encounters a consonant, it stops. Also, how would the same problem be solved using the ".each" method?

Upvotes: 3

Views: 4226

Answers (1)

Jordan Running
Jordan Running

Reputation: 106037

The problem is that you never reset j to zero.

The first time your outer while loop runs, which is to compare the first character of string to each vowel, j is incremented from 0 (for "a") to 4 (for "u"). The second time the outer loop runs, however, j is already 4, which means it then gets incremented to 5, 6, 7 and on and on. vowels[5], vowels[6], etc. all evaluate to nil, so characters after the first are never counted as vowels.

If you move the j = 0 line inside the outer while loop, your method works correctly.


Your second question, about .each, shows that you're already thinking along the right lines. while is rarely seen in Ruby and .each would definitely be an improvement. As it turns out, you can't call .each on a String (because the String class doesn't include Enumerable), so you have to turn it into an Array of characters first with the String#chars method. With that, your code would look like this:

def count_vowels(string)
  chars = string.chars
  vowels = ["a", "e", "i", "o", "u"]
  count = 0

  chars.each do |char|
    vowels.each do |vowel|
      if char == vowel
        count += 1
        break
      end
    end
  end

  puts count
end

In Ruby, though, we have much better ways to do this sort of thing. One that fits particularly well here is Array#count. It takes a block and evaluates it for each item in the array, then returns the number of items for which the block returned true. Using it we could write a method like this:

def count_vowels(string)
  chars = string.chars
  vowels = ["a", "e", "i", "o", "u"]

  count = chars.count do |char|
    is_vowel = false
    vowels.each do |vowel|
      if char == vowel
        is_vowel = true
        break
      end
    end

    is_vowel
  end

  puts count
end

That's not much shorter, though. Another great method we can use is Enumerable#any?. It evaluates the given block for each item in the array and returns true upon finding any item for which the block returns true. Using it makes our code super short, but still readable:

def count_vowels(string)
  chars = string.chars
  vowels = %w[ a e i o u ]

  count = chars.count do |char|
    vowels.any? {|vowel| char == vowel }
  end

  puts count
end

(Here you'll see I threw in another common Ruby idiom, the "percent literal" notation for creating an array: %w[ a e i o u ]. It's a common way to create an array of strings without all of those quotation marks and commas. You can read more about it here.)

Another way to do the same thing would be to use Enumerable#include?, which returns true if the array contains the given item:

def count_vowels(string)
  vowels = %w[ a e i o u ]  
  puts string.chars.count {|char| vowels.include?(char) }
end

...but as it turns out, String has an include? method, too, so we can do this instead:

def count_vowels(string)
  puts string.chars.count {|char| "aeiou".include?(char) }
end

Not bad! But I've saved the best for last. Ruby has a great method called String#count:

def count_vowels(string)
  puts string.count("aeiou")
end

Upvotes: 11

Related Questions