2

I want to do a function that counts the consonants in a string, so I tried to do this:

def vowel_count(foo):
  count = 0
  for i in foo:
    if not i == 'a' and i == 'e' and i ... and i == 'O' and i == 'U':
      count += 1
return count

But this is pretty ugly and tedious to do, more so with many more conditions. Is there any way to put them together?

Orión González
  • 309
  • 2
  • 14
  • 6
    `i.lower() in "aeiou"` ? – Rakesh Aug 13 '18 at 12:30
  • 2
    Also note that your original code does not do what you want. `not a and b and c` is understood as `(not a) and b and c`, which is quite different from `not (a and b and c)`. It is pretty hard for a character to be "e" and "O" and "U" all at the same time, even if it is not "a". – Amadan Aug 13 '18 at 12:32
  • `sum(c for k, c in collections.Counter(i.lower()) if k in 'aeiou')` – Chris_Rands Aug 13 '18 at 12:33

2 Answers2

6

You are looking for the not in operator.

def vowel_count(foo):
    count = 0
    for i in foo.lower():
        if i not in 'aeiou':
            count += 1
    return count

or more simply:

def vowel_count(foo):
    return sum(i not in 'aeiou' for i in foo.lower())  # True == 1, False == 0
chepner
  • 497,756
  • 71
  • 530
  • 681
  • is it better to build the set first once? Also I thought using `... if i not in` was normally faster than cooercing `i not in ...` from bool to int? – Chris_Rands Aug 13 '18 at 12:39
  • 1
    No need to do `set(...)` -- it's much slower. It's creating a set from the string on each iteration. You can use `set` operations instead. – warvariuc Aug 13 '18 at 12:39
  • @warvariuc I was working under the incorrect assumption that the optimizer would create the set at compile time. Also, though technically `in` is an O(n) operation for a string, it's such a short string that the string is still slightly faster than the set. – chepner Aug 13 '18 at 13:09
  • 1
    Or, in short, beware premature optimization ;) – chepner Aug 13 '18 at 13:10
  • @warvariuc: You could make a set of counted (or discounted) characters, either before the loop or even outside the function. I don't see why it would need to be constructed each iteration... The question is just whether `in` works better for `set` or for `str` (I suspect `set` but won't state it firmly without profiling) but you can definitely build a set without it impacting performance. – Amadan Aug 14 '18 at 07:31
  • @Amadan I timed `'x' in 'aeiou'` and `'x' in set_of_vowels`, and `str.__contains__` came back slightly faster for this small set. Switching from a set of vowels to a set of consonants is sufficient for the set to win. – chepner Aug 14 '18 at 12:43
0

Use a container to store vowels in their lower form:

def vowel_count(foo):
    vowel = ["a", "e", "i", "o", "u", "y"]
    count = 0
    for i in foo:
        if i.lower() not in vowel:
            count += 1

Then you could also do this a bit differently with a filter for instance.

def condition(letter):
    vowel = ["a", "e", "i", "o", "u", "y"]
    if letter.lower() not in vowel and letter != " ":
        return True
    else:
        return False

foo = 'bnojouearoui okeorjaji '
count = len(list(filter(condition, foo)))
Mathieu
  • 5,410
  • 6
  • 28
  • 55