Reputation: 2153
I have text:
Lorem ipsum %NAME0% dolor sit amet %NAME1%, consectetur %NAME2% adipiscing elit.
and array names
:
[Bob, Alice, Tom]
I need to get X
index from %NAMEX%
and replace all %NAME0%
, %NAME1%
, %NAME2%
with corresponding item from array: names.get(X)
.
I have
private fun String.replaceWithNames(names: List<String>): String {
var result = this
names.forEachIndexed { index, s ->
result = result.replace("%NAME$index%", s)
}
return result
}
but I am sure that it can be done more optimally.
Upvotes: 1
Views: 200
Reputation: 627335
You can use the following based on the kotlin.text.replace
function:
val pattern = """%NAME(\d+)%""".toRegex()
fun String.replaceWithNames(names: List<String>): String {
return this.replace(pattern, fun(m: MatchResult) : String {
return names.getOrNull(m.groupValues[1].toInt()) ?: m.value
})
}
Or,
val pattern = """%NAME(\d+)%""".toRegex()
fun String.replaceWithNames(names: List<String>): String {
return pattern.replace(this) { m ->
names.getOrNull(m.groupValues[1].toInt()) ?: m.value
}
}
See the online Kotlin demo:
import java.util.*
val pattern = """%NAME(\d+)%""".toRegex()
fun String.replaceWithNames(names: List<String>): String {
return pattern.replace(this) { m ->
names.getOrNull(m.groupValues[1].toInt()) ?: m.value
}
}
fun main(args: Array<String>) {
val lst = listOf("Bob", "Alice", "Tom")
println( "Lorem ipsum %NAME0% dolor sit amet %NAME1%, consectetur %NAME2% adipiscing elit. Wrong %NAME11%".replaceWithNames(lst) )
}
Output:
Lorem ipsum Bob dolor sit amet Alice, consectetur Tom adipiscing elit. Wrong %NAME11%
The %NAME(\d+)%
regex matches %NAME
, then captures one or more digits into Group 1, and then matches a %
char.
If names contains the index captured in Group 1, the replacement is the corresponding names
item, else, it is the whole match.
Upvotes: 2
Reputation: 37799
You could use a regex to go through the string only once and replace each value using the last Regex.replace overload (with lambda). Also, I added some validation in case there is a name reference with an out of bounds index (your current code would just leave the reference there).
private val nameRefRegex = Regex("""%NAME(\d)%""")
private fun String.replaceWithNames(names: List<String>): String {
return nameRefRegex.replace(this) { match ->
val index = match.groupValues[1].toInt()
require(index in names.indices) { "Invalid name index $index, only ${names.size} names available" }
names[index]
}
}
Note: if you need more than 10 names, you could change (\d)
to (\d+)
in the regex (to support more than 1 digit)
Upvotes: 4