0

I have a string with "?" as placeholders. I need to loop through the string and replace each "?" with the next value in a list.

For example:

my_str = "Take the source data from ? and pass to ?;"
params = ['temp_table', 'destination_table']

Here's what I've tried, which works:

params_counter = 0
for letter in my_str:
    if letter == '?':
        # Overwrite my_str with a new version, where the first ? is replaced
        my_str = my_str.replace('?', params[params_counter], 1)
        params_counter += 1

The problem is it seems rather slow to loop through every single letter in the string, multiple times. My example is a simple string but real situations could be strings that are much longer.

What are more elegant or efficient ways to accomplish this?

I saw this question, which is relatively similar, but they're using dictionaries and are replacing multiple values with multiple values, rather than my case which is replacing one value with multiple values.

S.S.
  • 684
  • 8
  • 21
  • Python has a built-in method for just this task. Check out the first answer to this question: https://stackoverflow.com/questions/9452108/how-to-use-string-replace-in-python-3-x – Philippe Dixon Jun 18 '20 at 15:58
  • @PhilippeDixon true, but .replace() doesn't give you multiple values to use as the replacements, if I understand it correctly... – S.S. Jun 18 '20 at 15:59
  • 1
    There was no point in looping over every character of your string. Just do the `.replace()` in a loop; it's not an error if there wasn't actually a `'?'` to replace. – jasonharper Jun 18 '20 at 16:00

2 Answers2

2

you don't need to iterate, replace will replace the first occcurrence of the string:

a clever solution is to split by the string you are searching, then zipping the list with a list of replacements of length the same and then joining

list1 = my_str.split('?')
params = ['temp_table', 'destination_table']
zipped = zip(list1, params)
replaced = ''.join([elt for sublist in zipped for elt in sublist])

In [19]: replaced                                                               
Out[19]: 'Take the source data from temp_table and pass to destination_table'

You can use multiple characters strings, which is what would kill your method:

my_str = "Take the source data from magicword and pass to magicword;
list1 = my_str.split('magicword') 
params = ['temp_table', 'destination_table'] 
zipped = zip(list1, params)
replaced = ''.join([elt for sublist in zipped for elt in sublist]) 
In [25]: replaced                                                               
Out[25]: 'Take the source data from temp_table and pass to destination_table'

Note that if your params are shorter than the number of occurrences of your search string this will cut it

IN []my_str = "Take the source data from ? and pass to ?; then do again with ? and to ?"
Out[22]: 'Take the source data from temp_table and pass to destination_table'

Also note that the last bit after finding the string is deleted :(, something like

replaced = ''.join([elt for sublist in zipped for elt in sublist] + [list1[-1]])

Will do the trick

E.Serra
  • 1,495
  • 11
  • 14
1

While @E. Serra's answer is a great answer, the comment from @jasonharper made me realize there's a MUCH simpler answer. So incredibly simple that I'm surprised that I completely missed it!

Instead of looping through the string, I should loop through the parameters. That'll allow me to replace the first instance of "?" with the current parameter that I'm looking at. Then I overwrite the string, allowing it to execute correctly on the next iteration.

Unlike the other solution posted, it won't cut off the end of my string either.

my_str = "Take the source data from ? and pass to ?;"
params = ['temp_table', 'destination_table']

for item in params:
    query_str = query_str.replace('?', item, 1)
S.S.
  • 684
  • 8
  • 21