6

I know you're able to link the .replace() method to make multiple substring replacements.

But there's a problem when I do this:

phrase = "AppleBananaCarrot"
print(phrase.replace("Banana","Apple").replace("Apple","Banana"))

Here, I wanted Banana and Apple to swap, so that it printed: BananaAppleCarrot

Instead it printed: BananaBananaCarrot

In other words: I don't want a replacement substring to be replaced again. The only way I see this being solved is if there was a way to use the .replace() method simultaneously instead of subsequently. Does anyone know how to do that or something similar?

I tried looking on stackoverflow, but the questions were just asking how to do it subsequently more efficiently. I want to do it simultaneously. I couldn't find a question for that.

  • 3
    Does this answer your question? [How to replace multiple substrings of a string?](https://stackoverflow.com/questions/6116978/how-to-replace-multiple-substrings-of-a-string) – Yevhen Kuzmovych Aug 22 '22 at 14:29
  • No, most of the comments are replacing strings subsequently instead of simultaneously. The question itself doesn't make that distinction, as I tried to make here. – Baron McDonald Aug 22 '22 at 14:32
  • What does doing something in python *simultaneously* mean to you? – LinFelix Aug 22 '22 at 14:39

6 Answers6

7

One trick we can try here is to use re.sub with a callback function:

import re

phrase = "AppleBananaCarrot"
output = re.sub(r'(?:apple|banana)', lambda m: 'Apple' if m.group().lower() == 'banana' else 'Banana', phrase, flags=re.I)
print(output)  # BananaAppleCarrot

The trick here is to make a single pass over the string and use logic to swap Apple for Banana and vice-versa. Note that we could try to do a three step replacement Apple -> (some other string) -> Banana, but the problem with this approach is that there is always the possibility that this intermediate string might happen to already be present in the input.

Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
2

This could be also a possible solution to that:

>>> f = lambda phrase, w1, w2: w2.join(word.replace(w2, w1) for word in phrase.split(w1))
>>> 
>>> phrase = "AppleBananaCarrot"
>>> print(f(phrase, 'Apple', 'Banana'))
BananaAppleCarrot
game0ver
  • 1,250
  • 9
  • 22
0

You will have to use placeholder (e.g. F1 and F2), otherwise you will not be able to distinguish between the original and the replaced substring. That is a solution if you want to only use String.replace.

phrase.replace("Banana","F1").replace("Apple","F2").replace("F1","Apple").replace("F2","Banana")
LinFelix
  • 1,026
  • 1
  • 13
  • 23
  • Here's the thing, the actual problem I'm dealing with is much more complex than the example I gave. I simplified it for the purposes of the question, but in my actual problem, I have to make a lot of replacements. Using the placeholder would take up too much code and would be prone to error. – Baron McDonald Aug 22 '22 at 14:39
  • You could generate that code with python and not be tied down to how many 'place holders` you felt like typing down – LinFelix Sep 05 '22 at 12:52
0

Here is my answer:

phrase = "AppleBananaCarrot"
print(phrase.replace("Banana","x").replace("Apple","Banana").replace("x", "Apple"))
osm
  • 39
  • 7
0

you can make a function to do LinFelix answer automatically. The trick is to generate placeholder and check if they are present in the initial string or in newlist. if so, discard and generate another one.

import random
import string

def super_replace(s, oldlist, newlist):
    if len(oldlist) != len(newlist):
        return
        
    #generating placeholders
    phlist = []
    while len(phlist) < len(oldlist):
        ph = "<{[" + ''.join(random.choices(string.ascii_letters + string.digits, k=8)) + "]}>"
        if ph not in s and ph not in phlist and ph not in ''.join(newlist):
            phlist.append(ph)
    
    #replace oldlist with placeholder
    for i in range(len(oldlist)):
        s = s.replace(oldlist[i], phlist[i])
    
    #replace placeholder with newlist
    for i in range(len(oldlist)):
        s = s.replace(phlist[i], newlist[i])
    
    return s
    
    
phrase = "AppleBananaCarrot"
new_phrase = super_replace(phrase, ["Apple", "Banana"], ["Banana", "Apple"])
print(new_phrase)

output:

BananaAppleCarrot

you can also tweak the generation of placeholder so it doesn't required importing random and string by, for example, converting an int to base64

To minimize risk of unwanted behaviour if your substring are small, you can also test if one of the "old" substring is contained in the placeholder.

discard = False
for old in oldlist:
    if old in ph:
        discard = True
if ph not in s and ph not in phlist and ph not in ''.join(newlist) and not discard:
    phlist.append(ph)
patate1684
  • 639
  • 3
  • 17
0

You may split the string on capital letters. Swap the first & second sub-strings, then re-join all the three sub-strings:

import re
# Splitting on UpperCase
split_cap = re.findall('[A-Z][^A-Z]*', phrase) 
split_cap[1]+split_cap[0]+split_cap[2]
osm
  • 39
  • 7