13
\$\begingroup\$

I've written the following code where all vowels are removed from a string except if the string starts with a vowel then this vowel is maintained.

def removeVowels(string):
    output = string[0]

    for char in string[1:]:
        if char.lower() not in 'aeuio':
            output += char
    return output

I was wondering if it's possible to use a list comprehension for this?

\$\endgroup\$
29
\$\begingroup\$

removeVowels is not quite an accurate name. Furthermore, by PEP 8, the official Python style guide, function names should be lower_case_with_underscores unless you have a good reason to deviate. Therefore, I recommend renaming the function to remove_non_initial_vowels.

Your function crashes on string[0] if the input is an empty string.

I don't recommend writing string[1:], since that would entail making a temporary copy of nearly the entire string.

'aeuio' is a bit weird. Why not 'aeiou'? (I assume that for this exercise, you don't care that y is sometimes a vowel, w is a semivowel, and assume that the string contains no diacritics.)

Fundamentally, this operation is a fancy string substitution. Typically, such substitutions are best done using regular expressions.

import re

def remove_non_initial_vowels(string):
    return re.sub('(?<!^)[aeiou]', '', string, flags=re.I)

The (?<!^) part of the expression is a negative lookbehind assertion that means "not at the beginning of the string".

\$\endgroup\$
  • 2
    \$\begingroup\$ I was too caught up in answering his question of using a list comprehension rather than looking for alternatives. +1 \$\endgroup\$ – randyr Jan 5 '17 at 8:44
  • \$\begingroup\$ For me, as a beginner, the anwser of Randyr is easier to understand. Thanks anyways :)! \$\endgroup\$ – Roelland Jan 5 '17 at 9:00
  • 8
    \$\begingroup\$ @Roelland Out of the three answers currently present, this to me seems like the best. Mostly because it works on any string you pass to the function and it's concise. The maketrans solution is very valid as well, but having to account for the python version which might be running the code can be a bit annoying for a small function such as this. I recommend you change the best answer to this one. \$\endgroup\$ – randyr Jan 5 '17 at 9:09
  • 3
    \$\begingroup\$ @Roelland: If you don't understand regex, this is a good excuse to learn it. It's an essential tool for working with strings in any language. \$\endgroup\$ – BlueRaja - Danny Pflughoeft Jan 5 '17 at 23:44
9
\$\begingroup\$

Just like your last question you can use str.translate:

You can do this with str.translate, which was changed slightly in Python 3. All you need to do is pass it a translation table, and it'll do what your above code is doing. However to build the translation table is hard in Python 2 and requires the helper function string.maketrans, which was changed to str.maketrans in Python 3.

However for this question, you don't need to use string.maketrans in Python 2, as str.translate takes a secondary argument of values to delete:

>>> 'abcdefghij'.translate(None, 'aeiou')
'bcdfghj'

However you do have to use str.maketrans in Python 3, as str.translate no longer has the second option:

>>> trans = str.maketrans('', '', 'aeiou')
>>> 'abcdefghij'.translate(trans)
'bcdfghj'
>>> trans = {ord('a'): None, ord('e'): None, ord('i'): None, ord('o'): None, ord('u'): None}
>>> 'abcdefghij'.translate(trans)
'bcdfghj'

The simplest way to use this would be to take the first letter and translate everything else:

>>> def removeVowels(string):
    trans = str.maketrans('', '', 'aeiouAEIOU')
    return string[0] + string[1:].translate(trans)

>>> removeVowels('Hello there')
'Hll thr'
>>> removeVowels('All, boo')
'All, b'
\$\endgroup\$
8
\$\begingroup\$

Even though I very like @200_success' answer and I recommend it, I’d like to expand on @Randyr's one.

Instead of rellying on string indices (which disallow the use of empty strings), we can iterate over the parameter using native Python tools: iter and next. This way, we allow any iterables of characters/strings to be passed as parameter:

FORBIDDEN = set('aeiou')


def remove_vowels(iterable):
    iterator = iter(iterable)
    return next(iterator, '') + ''.join([c for c in iterator if c.lower() not in FORBIDDEN])

We can even turn the function into a generator so the burden of concatenating the characters is left to the caller:

FORBIDDEN = set('aeiou')


def generate_removed_vowels(iterable):
    iterator = iter(iterable)
    yield next(iterator, '')
    for character in iterator:
        if character.lower() not in FORBIDDEN:
            yield character


def remove_vowels(iterable):
    return ''.join(generate_removed_vowels(iterable))
\$\endgroup\$
7
\$\begingroup\$

Yes, it is possible to do this using a list comprehension.

def removeVowel(string):
    output = string[0]

    for char in string[1:]:
        if char.lower() not in 'aeuio':
            output += char
    return output


def removeVowel2(string):
    return string[0] + ''.join([x for x in string[1:] if x.lower() not in 'aeuio'])


if __name__ == '__main__':
    # check the results are the same.
    example = 'Example sentence to use when comparing vowels'
    print(removeVowel(example) == removeVowel2(example))

string[0] is necessary to get the first letter. Otherwise we would have to build an extra check in the list comprehension to make sure we keep the first letter, which would make it a lot less readable.

''.join() is used here because [x for x in string if lower() not in 'aeuio'] returns a list of characters, rather than a string.

\$\endgroup\$
  • \$\begingroup\$ I've just checked and it should be like: def removeVowels(string): return string[0] + ''.join([x for x in string[1:] if x.lower() not in 'aeuio']) because else the first letter is doubled. \$\endgroup\$ – Roelland Jan 5 '17 at 8:53
  • 2
    \$\begingroup\$ As with the original code, removeVowel2('') crashes on string[0]. \$\endgroup\$ – 200_success Jan 5 '17 at 9:04
  • 1
    \$\begingroup\$ @Alex Yes, but I don't see where my function concats string inside the loop? \$\endgroup\$ – randyr Jan 5 '17 at 9:12
  • 1
    \$\begingroup\$ To remove the error on string[0] you can use string[:1], but it keeps the string[1:] problem. You can fix it by using string = iter(string) and return next(string, '') + ''.join([x for x in string ...]). \$\endgroup\$ – Peilonrayz Jan 5 '17 at 9:49
  • 1
    \$\begingroup\$ Inside your join you can use a generator instead of a list explicitly.. join(x for....) \$\endgroup\$ – Miguel Jan 5 '17 at 20:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.