-1

I am trying to return the index value of the first vowel in a string using these functions:

def is_vowel(c):
    v = 'aeiou'
    for l in v:
        if l == c:
            return True
        else:
            return False

def index_of_first_vowel(string):
    for l in string:
        if is_vowel(l):
            index = int(string.index(l))
            return index

So the string 'hello' should return 1.

I have tried many different ways of writing the second function and every time it returns None. Am I using the index() method correctly? Or is my logic off somewhere?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Garrett
  • 43
  • 5

2 Answers2

1

This should solve your problem, you don't need to loop in the list of letters in the function is_vowel, you can just look if c is in it.

Also, your loop would be interrupted at the first pass every time you don't pass an 'a'

>>> def is_vowel(c):
...     if c in "aeiou":
...             return True
...     return False
... 
>>> def index_of_first_vowel(string):
...     for l in string:
...             if is_vowel(l):
...                     index = int(string.index(l))
...                     return index
... 
>>> print(index_of_first_vowel("hello"))
1
tia.milani
  • 118
  • 1
  • 5
0

Note that your approach can be improved quite a bit:

  • The whole is_vowel() can be rewritten in one line (and considering also the case):
def is_vowel(c):
    return c in 'aeiouAEIOU'

and that can be easily inlined.

  • Instead of using str.index(), you can just keep track of the index at each iteration. The idiomatic way in Python is by using enumerate().
  • If you do not want your function to return None, you have to explicitly use return after your loop to handle the case of no character actually being a vowel.
  • The code can be made more general by inputing the "vowel" definition as a paremeter.

Putting all these together, one gets:

def find_any(s, chars='aeiouAEIOU'):
    for i, c in enumerate(s):
        if c in chars:
            return i
    return -1

For larger input strings, and small character sets a much more efficient approach is to use the start and stop parameters of str.index() or, possibly faster, str.find() which does not require a try/catch to handle missing matches:

def find_any_other(s, chars='aeiouAEIOU'):
    result = -1
    for char in chars:
        idx = s.find(char, 0, result)
        if idx > 0:
            result = idx
        if not idx:
            break
    return result if result < len(s) else -1

A simple test to show the timings indicate that the second approach can be much faster:

n = 1
s = 'x' * n + 'hello'
%timeit find_any(s)
# 1.35 µs ± 174 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit find_any_other(s)
# 6.04 µs ± 332 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

n = 1000000
s = 'x' * n + 'hello'
%timeit find_any(s)
# 172 ms ± 6.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit find_any_other(s)
# 790 µs ± 36.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
norok2
  • 25,683
  • 4
  • 73
  • 99