3

I'm looking for a more pythonic way to replace a known length of placeholders in a string with the values found in a list. They should replace in order and only be used once. For example with the values:

replaceVals = ['foo', 'bar']

origStr = 'I went to the ? to get ?'

I am looking to get:

newStr = 'I went to the foo to get bar'

I was able to get the desired result with the following loop, but I feel like there should be a better way to go about this than using a loop like so.

for i in range(len(replaceVals)):
   origStr = origStr.replace('?', replaceVals[i], 1)
yatu
  • 86,083
  • 12
  • 84
  • 139
jadki
  • 482
  • 1
  • 8
  • 15
  • 5
    `origStr = 'I went to the {} to get {}'.format(*replaceVals)` – roganjosh Feb 01 '19 at 21:43
  • This has to be a dupe but the closest I can find is https://stackoverflow.com/questions/4450592/is-there-a-python-equivalent-to-rubys-string-interpolation and it misses the most basic usage – roganjosh Feb 01 '19 at 21:45
  • Possible duplicate of [Using Python String Formatting with Lists](https://stackoverflow.com/questions/7568627/using-python-string-formatting-with-lists) – roganjosh Feb 01 '19 at 21:47
  • 5
    Do people get points for trying to point out duplicates? Just curious. Seems to be a real desire of late to not allow anything near a duplicate. I certainly don't want SO filled with the exact same question 100 times, but I don't mind 3 or 4 of pretty similar questions, just because it helps others with similar problems triangulate their own better. – Travis Griggs Feb 01 '19 at 21:48
  • @TravisGriggs no, but what's the point of having information rehashed in like 3 or 4 lines (which is becoming typical - "you can try this: ") when there is a single source of definitive info? – roganjosh Feb 01 '19 at 21:49
  • @TravisGriggs ... case in point – roganjosh Feb 01 '19 at 21:50
  • 1
    I guess I don't see @roganjosh. Like coding, I reduce/combine/refactor common patterns, once they've matured and I can prove that they really are. Premature "this looks like it's gonna be that" before it's matured, doesn't work for me. – Travis Griggs Feb 01 '19 at 21:53
  • @TravisGriggs What I'm suggesting is that the answer offers no explanation of why you should use the method proposed. The duplicate serves its purpose by directing different search terms to a particular source that I think provides some context for future viewers. We may disagree on this, fine, but it went exactly as I said: "Here's a way to do it" – roganjosh Feb 01 '19 at 21:55
  • 1
    I like your answer, just been burned too many times on SO lately where I post a question and someone slaps down the "looks like a duplicate folks" and often kills the chance of anyone looking at it any further. The "no, I went and looked at it and let me clarify why my case is different" ends up being too late. – Travis Griggs Feb 01 '19 at 21:57
  • @roganjosh This solution will work only if `origStr` does not contain a literal `{}`, in which case additional steps are needed to escape it. – blhsing Feb 01 '19 at 22:05
  • 2
    @TravisGriggs, maybe SO can have 3 types of dups: **1**. easy-to-find exact dups, should be downvoted and closed; **2**. hard-to-find exact dups, should be just closed with a reference link with no penalty to OP, some of us come to SO because we lack a certain knowledge, and due to lack of that very same knowledge, we couldn't recognize our questions were dups.; **3**. similar questions, still worth discussing with reference links to similar questions under it, think it as teachers giving students practice problems of slight variations but testing the same concept. – Indominus Feb 02 '19 at 00:13
  • Works for me @Indomimus – Travis Griggs Feb 02 '19 at 09:18

5 Answers5

2

Here's an idea using generators:

replaceVals = iter(['foo', 'bar'])
origStr = 'I went to the ? to get ?'

(' ').join(next(replaceVals) if i == '?' else i for i in origStr.split())

Output:

'I went to the foo to get bar'

The advantage of doing it this way, is that the amount of items in replaceVals does not have to match the amount of items to be replaced in origStr:

replaceVals = iter(['foo', 'bar', 'other'])
origStr = 'I went to the ? to get ?'

(' ').join(next(replaceVals) if i == '?' else i for i in origStr.split())
#'I went to the foo to get bar'

However using string formatting would cause errors under these circumstances.

yatu
  • 86,083
  • 12
  • 84
  • 139
2

You can use the replace and format methods of the string as shown below:

origStr.replace('?','{}').format(*replaceVals)

Out[334]: 'I went to the foo to get bar'
Onyambu
  • 67,392
  • 3
  • 24
  • 53
1

@roganjosh 's answer in comments is possibly the best, though the OP is abstract enough that it's not clear what his real case is. I was curious whether one could do this with f-strings which showed up in Python3. What makes an f-string less appealing than @roganjosh's, is that it's just so easy to unpack the replacement sequence through the .format() call. That said, IF you wanted to try an f-string, something like this would work:

replaceVals = ['foo', 'bar'] 
stream = iter(replaceVals)
f'I went to the {stream.next()} to get {stream.next()}'
Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
0

f string

r=["foo","bar"]
origStr = f'I went to the {r[0]} to get {r[1]}'

origStr
Out[21]: 'I went to the foo to get bar'
JoePythonKing
  • 1,080
  • 1
  • 9
  • 18
0

You can split the string with '?', then use itertools.zip_longest to pair substrings in the resulting list with replacement strings in replaceVals with an empty string as a fill value, and join the pairs of strings after flattening them with a generator expression:

from itertools import zip_longest
''.join(i for p in zip_longest(origStr.split('?'), replaceVals, fillvalue='') for i in p)
blhsing
  • 91,368
  • 6
  • 71
  • 106