Counting the number of unique vowels in a string

Viewed 768

I have the following task:

Import the sys module and create a function with a string parameter that will print the number of unique vowels in the string regardless of whether it is uppercase or lowercase.

For example:

Test 1: The argument swEet should print 1
Test 2: The argument “Aaa aeeE” should print 2
Test 3: The argument "eiOuayOI j_#Ra" should print 5

I have the following code:

import sys

def count_vowels(args):
    vowels = set()

    for arg in args:
        for char in arg:
            if char.lower() in 'aeiou':
                vowels.add(char)
    return len(vowels)

print(count_vowels(sys.argv[1:]))

Test case 1 is the only test that prints the expected output. The other two test cases, Test 2 and Test 3, do not print the correct expected result. Test 2 prints 5 rather the expected output of 4 and Test 3 prints 6 instead of the expected output of 5.

5 Answers

I think you're overcomplicating things. To get the number of unique vowels, you can lowercase the entire string, then call set() on the lowercased string to get a set of unique letters. You can then check whether each vowel is in this set. If it is, it appears in the text, otherwise it doesn't:

def count_vowels(text):
    letters = set(text.lower())
    
    count = 0
    for vowel in 'aeiou':
        if vowel in letters:
            count += 1
    return count

print(count_vowels("Aaa aeeE")) # Prints 2
print(count_vowels("eiOuayOI j_#Ra")) # Prints 5

If you prefer a one-liner, you can do this:

def count_vowels(text):
    return sum(1 for vowel in 'aeiou' if vowel in set(text.lower()))

or this (as suggested by psmears):

def count_vowels(text):
    return len(set(text.lower()) & set('aeiou'))

You are still mixing upper/lower casing, see other answers.

There is a lot shorter version for this

def count_vowels(word):
    # all letters you are interested in 
    allowed = frozenset("aeiou")
    # get the len of the intersection between allowed and lower cased word
    return len(allowed.intersection( word.lower()))

tests = [("swEet",1), ("Aaa aeeE", 2),("eiOuayOI j_#Ra", 5)]

for t in tests:
    print(t[0], "is", count_vowels(t[0]), "should be", t[1])

Output:

swEet is 1 should be 1
Aaa aeeE is 2 should be 2
eiOuayOI j_#Ra is 5 should be 5

This uses (frozen)set.intersection - and returns its length. frozenset is just a non-mutable version of set.

Change vowels.add(char) to vowels.add(char.lower())

You should also transform the char you append to the vowels set() to .lower(), since now it's adding the vowel as is, so the set can contain both o and O:

vowels.add(char.lower())

The other answers and comments explain the problem well, you should use a variable to count the occurrences of each vowel. Here's a short solution using list comprehension instead of a for loop:

def count_vowels(args: str):
    args = set(args.lower())
    return sum([vowel in args for vowel in 'aeiou'])

You can even go as far as to make it a lambda function:

count_vowels = lambda args: sum([vowel in set(args.lower()) for vowel in 'aeiou'])
Related