I'm looking for a way to keep track of variables used when doing a string interpolation without parsing the string. For example, if I have a string:
val expStr = "${var1} some other useless text ${var2}"
I want to be able to identify the order of the variables used, again without parsing the string. In this case [var1, var2] would be an expected output. So far I've thought of defining a class where I pass it all of the variables. Then reference said variables through the class function grab
.
val wrapper = ContainerClass(var1, var2)
val expStr = "${wrapper.grab(var1)} some other useless text ${wrapper.grab(var2)}"
inside ContainerClass
is a array, each time a variable is referenced it is added to the array and outputted through getReferenceCalls
val whatIWant = wrapper.getReferenceCalls() // [var1, var2]
This works great until I introduce the injection of strings into strings.
val wrapper = ContainerClass(var1, var2, var3)
val snippet = "${wrapper.grab(var1)} some other useless text ${wrapper.grab(var2)}"
val expStr = "${wrapper.grab(var3)} ${snippet}"
val notWhatIWant = wrapper.getReferenceCalls() // [var1, var2, var3]
Here, I want to identify the order of the injected variables in the final expStr ie. [var3, var1, var2]. My question is, is this possible without parsing expStr? I did also think of a not so elegant solution of allowing my class to define any given "snippet" and the class identifies the variables referenced in the snippet. This works but becomes convoluted fast. What I really need is an eligant solution...if it exists.
CodePudding user response:
I have implemented a "ContainerClass" to achieve your goal. I uses String.format
instead of string templates so that I don't need prior information of the input.
class StringNode(private val format: String, vararg args : Any) {
private val argv = args
override fun toString() : String = String.format(format,*argv)
fun getFlatArgs() : List<Any> = argv.flatMap {
if(it is StringNode){
it.getFlatArgs()
} else{
listOf(it)
}
}
}
Usage:
fun main(){
val sn1 = StringNode("1:%s 2:%s 3:%s","abc",123,"def")
println(sn1)
println(sn1.getFlatArgs())
val sn2 = StringNode("foo:%s bar:%s","foo",sn1);
println(sn2)
println(sn2.getFlatArgs())
val sn3 = StringNode("sn1:%s, sn2:%s",sn1,sn2);
println(sn3)
println(sn3.getFlatArgs())
}
Output:
1:abc 2:123 3:def
[abc, 123, def]
foo:foo bar:1:abc 2:123 3:def
[foo, abc, 123, def]
sn1:1:abc 2:123 3:def, sn2:foo:foo bar:1:abc 2:123 3:def
[abc, 123, def, foo, abc, 123, def]
CodePudding user response:
val var1 = "abc"
val var2 = "def"
val list = mutableListOf<String>()
val expStr = "${var1.also { list.add(it) }} some other useless text ${var2.also { list.add(it) }}"
println(expStr) // Output: "abc some other useless text def"
println(list) // Output: [abc, def]
Or:
val var1 = "abc"
val var2 = "def"
val list = mutableListOf<String>()
fun String.addTo(list: MutableList<String>) = this.also { list.add(it) }
val expStr = "${var1.addTo(list)} some other useless text ${var2.addTo(list)}"
println(expStr) // Output: "abc some other useless text def"
println(list) // Output: [abc, def]