3

I'm currently studying Python 2.7 from an online course. One of the problems is I have to remove a char from a string based from a list of chars.

What I did was:

def getAvailableLetters(letters):

    alphabet = string.ascii_lowercase
    reduced_alphabet = ''

    for char in alphabet:
        if char not in lettersGuessed:
            reduced_alphabet += char

    return reduced_alphabet

I've learned that there's no such thing as a string method to directly remove a char from a string as they are immutable, so I came up with this. I've successfully submitted a correct answer, but I'm not quite satisfied with it as I feel like there's a more efficient way to do it.

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
Hero Stradivari
  • 565
  • 1
  • 6
  • 13
  • Because the answers there apply directly to your problem; replace `string.punctuation` with (a string created from) `lettersGuessed`. – Martijn Pieters Nov 08 '13 at 12:18

3 Answers3

7

Fastest way would be to use str.translate here:

>>> lettersGuessed = ['a', 'b', 'c']
>>> 'wedqwdasdasccdshjasdcas'.translate(None, ''.join(lettersGuessed))
'wedqwdsdsdshjsds'

if lettersGuessed is already a string then remove the ''.join call.

Timing results compared to str.join and filter, taken from @thefourtheye's solution:

def getAvailableLetters2(lettersGuessed):
    return string.ascii_lowercase.translate(None, lettersGuessed)

from timeit import timeit
print 'filter-->', timeit("getAvailableLetters('Welcome')", setup="from __main__ import getAvailableLetters")
print '.join-->', timeit("getAvailableLetters1('Welcome')",setup="from __main__ import getAvailableLetters1")
print 'trans-->', timeit("getAvailableLetters2('Welcome')",setup="from __main__ import getAvailableLetters2")

Output:

filter--> 6.49355100548
.join--> 4.02496357229
trans--> 0.69938109531
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
4

You can use list comprehension like this, to filter out the characters which are in lettersGuessed

import string
def getAvailableLetters(lettersGuessed):
    return "".join([char for char in string.ascii_lowercase if char not in lettersGuessed])

print getAvailableLetters("Welcome")

Output

abdfghijknpqrstuvwxyz

Alternatively, you can use filter function like this

import string
def getAvailableLetters(lettersGuessed):
    return filter(lambda x: x not in lettersGuessed, string.ascii_lowercase)

print getAvailableLetters("Welcome")

Output

abdfghijknpqrstuvwxyz

Edit: Performance comparison

import string
def getAvailableLetters(lettersGuessed):
    return filter(lambda x: x not in lettersGuessed, string.ascii_lowercase)

def getAvailableLetters1(lettersGuessed):
    return "".join([char for char in string.ascii_lowercase if char not in lettersGuessed])

from timeit import timeit
print timeit("getAvailableLetters('Welcome')", setup="from __main__ import getAvailableLetters")
print timeit("getAvailableLetters1('Welcome')",setup="from __main__ import getAvailableLetters1")

Output on my machine

3.02976298332
2.00461006165

It shows that, list comprehension method beats filter method.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
2

The most efficient way is to use str.join() to turn a sequence of characters into a new string:

return ''.join([char for char in string.ascii_lowercase if char not in lettersGuessed])

This avoids creating a new string object every iteration of the loop; instead, the new string is created once.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343