tags:

views:

390

answers:

5

Is there a far shorter way to write the following code?

my_string = my_string.replace('A', '1')
my_string = my_string.replace('B', '2')
my_string = my_string.replace('C', '3')
my_string = my_string.replace('D', '4')
my_string = my_string.replace('E', '5')

Note that I don't need those exact values replaced, I'm simply looking for a way to turn 5+ lines into fewer than 5

+6  A: 
replaceDict = {'A':'1','B':'2','C':'3','D':'4','E':'5'}       
for key, replacement in replaceDict.items():  
  my_string = my_string.replace( key, replacement )
Stefan Kendall
+11  A: 

Looks like a good opportunity to use a loop:

mapping = { 'A':'1', 'B':'2', 'C':'3', 'D':'4', 'E':'5'}
for k, v in mapping.iteritems():
    my_string = my_string.replace(k, v)

A faster approach if you don't mind the parentheses would be:

mapping = [ ('A', '1'), ('B', '2'), ('C', '3'), ('D', '4'), ('E', '5') ]
for k, v in mapping:
    my_string = my_string.replace(k, v)
Rick Copeland
It's odd; that's the way I'd write it, because the problem is all about mapping from one character to another.But really, you're not using the dict as a dict; you're using it as a list of tuples. So why not write a list of tuples in the first place?
John Fouhy
Certainly a list of tuples would also work (and be faster). I'll edit the answer to include that option.
Rick Copeland
+15  A: 

Also look into str.translate(). It replaces characters according to a mapping you provide for Unicode strings, or otherwise must be told what to replace each character from chr(0) to chr(255) with.

Artelius
+19  A: 

You can easily use string.maketrans() to create the mapping string to pass to str.translate():

import string
trans = string.maketrans("ABCDE","12345")
my_string = my_string.translate(trans)
Matthew Schinckel
this is nice, but doesn't work in the case of multi-character patterns or replacements
Rick Copeland
Correct. I assumed from the question that it was character-based replacement.
Matthew Schinckel
saves me a question, thx
ohho
+3  A: 

If you want to get the wrong answer, slowly, then use string.replace in a loop. (Though it does work in this case of no overlap among the patterns and replacements.)

For the general case with possible overlaps or a long subject string, use re.sub:

import re

def multisub(subs, subject):
    "Simultaneously perform all substitutions on the subject string."
    pattern = '|'.join('(%s)' % re.escape(p) for p, s in subs)
    substs = [s for p, s in subs]
    replace = lambda m: substs[m.lastindex - 1]
    return re.sub(pattern, replace, subject)

>>> multisub([('hi', 'bye'), ('bye', 'hi')], 'hi and bye')
'bye and hi'

For the special case of 1-character patterns and 1- or 0-character replacements, use string.maketrans.

Darius Bacon
+1 on the approach of using re.sub, -1 for saying that `string.replace` gives the wrong answer, since it's not clear exactly what *should* happen for overlapping matches. So net 0 :)
Rick Copeland
str.replace can give an arguably wrong answer. For dictionary-based solutions, consider the string "wow" and translation table {'w': 'foo', 'o': 'bar'}. Depending on the order that the dict iterates, you can end up with different results: "fbarbarbarfbarbar" or "foobarfoo". A function that gives different output for equal input is arguably broken.
Miles
Miles, yes, that's the kind of problem I meant. You could also run into overlaps in the keys alone, like 'hello' and 'hell'.
Darius Bacon