I will be adding a function to my M_String module that will try to emulate the kind of functionality that some other languages provide, to wit, the ability to put a variable into a string so it is evaluated.
In Swift, for example, you can do this:
var personName="Kem"
println("Hi \\(personName)!")
// prints "Hi Kem!"
Naturally I can’t do quite that in a Xojo function, but I can do something similar to the way a prepared statement works, so you can do something like:
MsgBox MyFunction( "Hi \\?!", "Kem" )
That’s a simple example. With Variants, you should be able to do:
MsgBox MyFunction( "The \\? \\? the \\? by a score of \\? to \\?", "Yankees", "beat", "Red Sox", 15, 1 )
// "The Yankees beat the Red Sox by a score of 15 to 1"
But what to call “MyFunction”? Some thoughts:
Inject
Prepare
Since the function will end with “_MTC” it should be fairly short for convenience but still descriptive. Suggestions?
In other languages, this is called String Expansion. How about Expand()?
BTW, one thing with PostgreSQL expansion is it uses $1, $2, $3… So, in cases like that you can:
MsgBox Expand("Hello $1, I hope you are having a great time. $1, be sure to visit $2 while in town!", "Kem", "McDonalds")
It is extra work though if you go back and refactor your parameter list. i.e. changing $1’s to $2’s if inserting something at the start. Just a thought.
Oh… One more thing about $1 and $2… If you are using your source string as a constant and your app is multi-lingual… Some languages may have various orders. Using $1, $2 would enable the programmer to rearrange order in the output string.
Jeremy, Expand is good, I’ll think on that. And I like the other suggestion too. I hadn’t considered the cases where you might want the same variable injected in two different spots or in different order. I just have to think about cases where the number of variables gets into the double-digits so $1 and $10 don’t get confused.
Oh, of course, I’ll work backwards.
Tim, I appreciate the suggestions (well, the second one anyway). Prep is too short for my liking, so I’ll never remember what it means. I’m leaning towards Inject but Expand is good too.
Just allow a maximum of 9 variables, $1 - $9. I can’t think of any sane use case for needing more than 9 variables in a one-line substitution function. Btw, it’s sometimes called substitution/substitute.
I’ve also seen it done with {1} instead of $1. (E.g., Python.)
[quote=101995:@Kem Tekinay]I just have to think about cases where the number of variables gets into the double-digits so $1 and $10 don’t get confused.
Tim, I appreciate the suggestions (well, the second one anyway). Prep is too short for my liking, so I’ll never remember what it means. I’m leaning towards Inject but Expand is good too.[/quote]
Could just take the easy way out and limit it to 9 replacements.
This is the code I came up with, complete with comments. I haven’t yet tested for speed. Suggestions welcome.
Function Expand_MTC(src As String, ParamArray vars() As Variant) As String
// Takes a string and replaces the occurrences of a token with the values of the
// Variants. Those Variants must be able to get a string value.
// Values that exceed the number of tokens will be ignored.
//
// Two forms of tokens are accepted. If "\\?" is present in the string, it will
// replace those in order. Otherwise, it will look for the form "${#}"
// where # represents a specific value in the array.
//
// Examples:
// s = Expand_MTC( "The ${1} beat the ${2} by a score of ${3} to ${4}", "Yankees", "Red Sox", 15, 1 )
// s will be "The Yankees beat the Red Sox by a score of 15 to 1"
// s = Expand_MTC( "\\? is a great \\?", "Derek Jeter", "shortstop" )
// s will be "Derek Jeter is a great shortstop"
if vars.Ubound = -1 then return src
if src = EmptyString then return src
const kToken = "\\?"
if src.InStr( kToken ) = 0 then
// Using the "${#}" form
for i as Integer = 0 to vars.Ubound
dim token as string = "${" + str( i + 1 ) + "}"
src = src.ReplaceAll( token, vars( i ).StringValue )
next i
else // Using the "\\?" form
dim srcArr() as string = src.Split( kToken )
dim resultArr() as string
if srcArr.Ubound <> -1 then
resultArr.Append srcArr( 0 )
dim srcIndex as Integer = 1
dim varIndex as Integer = 0
do until srcIndex > srcArr.Ubound or varIndex > vars.Ubound
dim thisVar as String = vars( varIndex ).StringValue
varIndex = varIndex + 1
resultArr.Append thisVar
resultArr.Append srcArr( srcIndex )
srcIndex = srcIndex + 1
loop
// Add any missing source
for i as Integer = srcIndex to srcArr.Ubound
resultArr.Append kToken
resultArr.Append srcArr( i )
next i
src = join( resultArr, "" )
end if
end if
return src
End Function
[quote=101976:@Kem Tekinay]MsgBox MyFunction( “The \? \? the \? by a score of \? to \?”, “Yankees”, “beat”, “Red Sox”, 15, 1 )
// “The Yankees beat the Red Sox by a score of 15 to 1”[/quote]
I’m against nonsensical naming. Xojo is English based, and Kem in English have no meaning, but in Vietnamese means Cream.
“Expand” is good enough.[quote=102004:@Kem Tekinay]
If “\?” is present, it will be used in order. Otherwise, it will look for the “$#” form.[/quote]
I would suggest you trying to keep the sequential and positional syntax similar, like:
“my name is \? and my dog is \?”, name, dog
“my name is \2? and my dog is \1?”, dog, name
“my name is \2? and my dog is \1? and the name 37 is \37?”, arrayOfNames
I thought about that, but if the second value contains, for example, “$100”, then the next pass will incorrectly replace that with the contents of the first value. I also want to avoid ambiguity if a token is pressed up against a number like “$132”.
However, after sleeping on it, I think I might split the string and replace the parts rather than using ReplaceAll. That way, if value 1 contains “${2}”, it won’t be replaced on the next pass. Highly unlikely, but I like being thorough.