11
\$\begingroup\$

The objective was the following to be solved in Python:

Given a string, def a function that returns another string with the vowels (upper or lowercase) replaced by 0,1,2,3,4 respectively.


Example input:

"bB nN aei ou AEIOU"

Desired Output:

"bB nN 012 34 01234"

This is in fact quite trivial and can be solved in several ways, one way may be this:

def crypt(s):
    vow = ["a","e","i","o","u"]
    stringL = []

    for x in s:
        if x in vow or x.lower() in vow:
            stringL.append(str(vow.index(x.lower())))
        else:
            stringL.append(x)

    return "".join(stringL)

I was told this was over-complicated for such a simple task, that code like this would be difficult to debug etc.

Would you consider this approach a "bad" one, which way would you have gone instead, is it unclear?

\$\endgroup\$

4 Answers 4

14
\$\begingroup\$

Use string.maketrans().

from string import maketrans 

input = "aeiouAEIOU"
output = '0123401234'
trans = maketrans(input,output)
str = 'This is a Q&A site, not a discussiOn forUm, so please make sure you answer the question.'
print str.translate(trans)

Output:

Th2s 2s 0 Q&0 s2t1, n3t 0 d2sc4ss23n f3r4m, s3 pl10s1 m0k1 s4r1 y34 0nsw1r th1 q41st23n.
\$\endgroup\$
0
6
\$\begingroup\$

The calls to append, index, join, str are just noise.

Here's the same idea but with a map and a generator expression:

def crypt(s):
    m = {"A": "0", "E": "1", "I": "2", "O": "3", "U": "4"}
    return "".join(m[c.upper()] if c.upper() in m else c for c in s)

It's a little less noisy.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Use the dictionary .get() method instead the m[x] if x in m else y bit. \$\endgroup\$ Commented Apr 30, 2011 at 1:36
3
\$\begingroup\$

Yet another way to do this using duck typing.

def crypt(s):
    LUT = {"A": "0", "E": "1", "I": "2", "O": "3", "U": "4"}
    ns = ""
    for x in s:
        try:
            ns += LUT[x.upper()]
        except KeyError:
            ns += x
    return ns

Just for the sake of it I decided to run each method presented here for 100000 cycles with timeit.

The results were interesting, learnCodes was the fastest by a long shot.

    mine 1.431309

    ribby 1.314431

    pat 0.631507

    learnCode 0.124485

#my method
def crypt(s):
    LUT = {"A": "0", "E": "1", "I": "2", "O": "3", "U": "4"}
    ns = ""
    for x in s:
        try:
            ns += LUT[x.upper()]
        except:
            ns += x
    return ns

#ribbys method
def crypt2(s):
    m = {"A": "0", "E": "1", "I": "2", "O": "3", "U": "4"}
    return "".join(m[c.upper()] if c.upper() in m else c for c in s)

#pats method
def crypt3(s):
  substitutions = {
    'a' :  '0',
    'e' :  '1',
    'i' :  '2',
    'o' :  '3',
    'u' :  '4',
    'A' :  '0',
    'E' :  '1',
    'I' :  '2',
    'O' :  '3',
    'U' :  '4'
  }
  for before, after in substitutions.items():
    s =  s.replace(before, after)
  return s

#learnCodes method
from string import maketrans 
def crypt4(s):
    input = "aeiouAEIOU"
    output = '1234512345'
    trans = maketrans(input,output)
    return s.translate(trans)

import timeit
print "volting %f" %timeit.Timer("crypt('bB nN aei ou AEIOU')", "from __main__ import crypt").timeit(100000)
print "ribby %f" %timeit.Timer("crypt2('bB nN aei ou AEIOU')", "from __main__ import crypt2").timeit(100000)
print "pat %f" %timeit.Timer("crypt3('bB nN aei ou AEIOU')", "from __main__ import crypt3").timeit(100000)
print "learnCode %f" %timeit.Timer("crypt4('bB nN aei ou AEIOU')", "from __main__ import crypt4").timeit(100000)
\$\endgroup\$
5
  • 1
    \$\begingroup\$ Please don't use except: you'll catch any exception. Catch the KeyError specifically. \$\endgroup\$ Commented Apr 30, 2011 at 13:48
  • \$\begingroup\$ I usually don't... it slipped my mind! \$\endgroup\$
    – volting
    Commented Apr 30, 2011 at 14:32
  • \$\begingroup\$ That's interesting, thanks for doing the comparison. :) I wonder why maketrans is so fast? \$\endgroup\$
    – pat
    Commented Apr 30, 2011 at 21:54
  • \$\begingroup\$ Thank you for another giving another approach and for taking the time for timing them all! +1! (BTW which machine did you run it in? It's so much faster than mine!) \$\endgroup\$
    – Trufa
    Commented May 1, 2011 at 0:41
  • \$\begingroup\$ @Trufa your welcome! The test machine is nothing special just an E8500 with 8GB RAM running Windows 7 x64 \$\endgroup\$
    – volting
    Commented May 1, 2011 at 11:59
2
\$\begingroup\$

I think in this case you're best off just writing out the mapping -- there are only ten letter/digit pairs, and writing code to generate that mapping doesn't buy you much.

def crypt(s):
  substitutions = {
    'a' :  '0',
    'e' :  '1',
    'i' :  '2',
    'o' :  '3',
    'u' :  '4',
    'A' :  '0',
    'E' :  '1',
    'I' :  '2',
    'O' :  '3',
    'U' :  '4'
  }

  for before, after in substitutions.items():
    s =  s.replace(before, after)

  return s

If you were writing a more general transliteration tool, say, with more complicated substitution rules, then the situation would be different. It's fun to come up with neat ways to build that mapping... for instance:

substitutions = dict(zip('aeiou','01234') + zip('AEIOU', '01234'))

Cute, except it isn't -- you're going to hate yourself in a couple weeks (or days!) when someone comes along with "AND SOMETIMES Y!!!", right?

Given the small scale of the problem statement, I'd say just spit out the mapping and be done with it.

\$\endgroup\$
1
  • \$\begingroup\$ Nice answer thanks! just spit out the mapping and be done with it. agreed! This was a quick exercise we did on class, just got hung up with it. Thanks again. \$\endgroup\$
    – Trufa
    Commented Apr 30, 2011 at 6:06

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