Kotlin Data Class Parsing Mutiple member properties at once - kotlin

Let say I have a data class called Class A
data class ClassA {
val x0 = ""
val x1 = "some string"
val x2 = "some string"
val x3 = "some string"
val x4 = "some string"
val x5 = ""
val x6 = ""
val y = ""
val z = ""
}
I can retrieve the value of these class member thru its class object
val obj = ClassA()
// if the properties has prefix x and not empty then concatenate to this x variable
val x = obj.x1 + obj.x2 + etc...
...
Let say if this data class has 50+ x(n) in it and I want to retrieve any member that does not have an "empty string or null" and match the prefix then how do I do it dynamically (can be a for-loop), instead of type out statically every single properties that I want to retrieve, is there an alternative way to do it?

You could solve that with Reflection:
data class ClassA (
val x0: String = "",
val x1: String = "some string 1",
val x2: String = "some string 2",
val x3: String = "some string 3",
val x4: String = "some string 4",
val x5: String = "",
val x6: String = "",
val y: String = "",
val z: String = ""
)
val obj = ClassA()
val result = ClassA::class.java.declaredFields // or:
.filter { it.name.startsWith("x") }
.onEach { it.isAccessible = true }
.map { it.get(obj) }
.joinToString("; ")
println(result)
This will print:
; some string 1; some string 2; some string 3; some string 4; ;
If you want to omit x0, x5, and x6, which all are empty strings, and you want to concatenate without semicolon:
val result = Class::class.java.declaredFields
.filter { it.name.startsWith("x") }
.onEach { it.isAccessible = true }
.map { it.get(obj) }
.filter { it != "" }
.joinToString("")

I'm going to answer you question and provide a way of doing it, however, I highly recommend against doing it this way simply because while it works, it is seen as hacky and you'd be better off solving this by changing the multiple fields/properties into a collection of some sort instead.
You can use reflection to do it without having to type out all of the values manually.
To do it via reflection, you will have to do something like this.
fun getNonEmptyStrings(input: A): List<String> {
val members = A::class.memberProperties
return members
.filter { it.name.startsWith("x") }
.map { it.get(input) }
.filterIsInstance<String>()
.filter { it.isNotBlank() }
}
There might be some other form of meta-programming that is better suited to this, but I'm not sure what that would be.

Related

Returning one of different object types from single function in kotlin

I have the following structure at present:
#Entity
#Table(name = "table_app_settings")
data class AppSetting(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "app_setting_id")
val id: Long? = null,
#Column(name = "app_setting_name")
val name: String = "",
#Column(name = "app_setting_value")
var value: String = "",
#Column(name = "app_setting_type")
val type: AppSettingType,
)
enum class AppSettingType {
CHAR,
STRING,
BYTE,
SHORT,
INT,
LONG,
DOUBLE,
FLOAT,
BOOLEAN,
}
This is then saved to the database with the following:
override fun saveAppSetting(setting: AppSetting): DatabaseResult<AppSetting> {
log.info("Saving App Setting ${setting.name} to database.")
return try {
// Attempt to save the entity to the database. If we do not throw an exception, return success.
val savedSetting = appSettingsRepository.save(setting)
DatabaseResult(
code = ResultCode.CREATION_SUCCESS,
entity = savedSetting
)
} catch(exception: DataAccessException) {
log.error("Unable to save App Setting ${setting.name} to database. Reason: ${exception.message}")
DatabaseResult(
code = ResultCode.CREATION_FAILURE
)
}
}
Now, let's say that I wish to save a Char type to database, I figure I would use the following:
override fun saveAppSetting(name: String, value: Char): DatabaseResult<Char> {
val appSettingResult = saveAppSetting(AppSetting(
name = name,
value = value.toString(),
type = AppSettingType.CHAR,
))
return if(appSettingResult.code != ResultCode.CREATION_FAILURE) {
val entity = getAppSetting<Char>(appSettingResult.entity?.name!!).entity.toString().first()
DatabaseResult(
code = appSettingResult.code,
entity = entity
)
} else {
DatabaseResult(
code = ResultCode.CREATION_FAILURE,
)
}
}
I also figured that I would need to do the following in order to retrieve the correct object type:
override fun getAppSetting(name: String): DatabaseResult<Any?> {
log.info("Getting App Setting $name from database.")
val appSetting = appSettingsRepository.findAppSettingByName(name)
return if(appSetting != null) {
log.info("App Setting $name has ID of ${appSetting.id} within the database")
when(appSetting.type) {
AppSettingType.CHAR -> {
DatabaseResult<Char>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.first(),
)
}
AppSettingType.STRING -> {
DatabaseResult<String>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value,
)
}
AppSettingType.BYTE -> {
DatabaseResult<Byte>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toByte(),
)
}
AppSettingType.SHORT -> {
DatabaseResult<Short>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toShort(),
)
}
AppSettingType.INT -> {
DatabaseResult<Int>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toInt(),
)
}
AppSettingType.LONG -> {
DatabaseResult<Long>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toLong(),
)
}
AppSettingType.DOUBLE -> {
DatabaseResult<Double>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toDouble(),
)
}
AppSettingType.FLOAT -> {
DatabaseResult<Float>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toFloat()
)
}
AppSettingType.BOOLEAN -> {
DatabaseResult<Boolean>(
code = ResultCode.FETCH_SUCCESS,
entity = appSetting.value.toBoolean()
)
}
}
} else {
log.error("App Setting $name does not seem to exist within the database.")
DatabaseResult(
code = ResultCode.FETCH_FAILURE
)
}
However, when I then wish to use said object, I still have to write something like the following:
val newBarcode = getAppSetting("barcode_value").entity.toString().toInt()
Assuming I've "initialised" barcode_value with a value of 177 (for example).
How can I get the function to return what I need without having to do .toString.to...()?
Yes this all possible, here is a simplified demo, firstly
import kotlin.reflect.KClass
data class AppSetting(
val id: Long? = null,
val name: String = "",
var value: String = "",
val type: AppSettingType,
)
enum class AppSettingType(val clazz: KClass<out Any>) {
CHAR(Char::class),
STRING(String::class),
INT(Int::class),
}
So I added a clazz so from the enum we know the Kotlin type
and now a function to simulate your repository fetch
fun findAppSettingByName(name: String): AppSetting? {
return when(name) {
"Char thing" -> AppSetting(value= "C", type = AppSettingType.CHAR)
"String thing" -> AppSetting(value= "Str", type = AppSettingType.STRING)
"Int thing" -> AppSetting(value= "42", type = AppSettingType.INT)
else -> throw IllegalArgumentException()
}
}
Next in the function declaration I have made it generic with T and for the purposes of the demo removed the DatabaseResult container. Then I added a clazz parameter which is the typical Java way of carrying the required class information into the function:
fun <T : Any> getAppSetting(name: String, clazz: KClass<T>): T? {
val appSetting: AppSetting? = findAppSettingByName(name)
return appSetting?.let {
require(clazz == appSetting.type.clazz) {
"appSetting.type=${appSetting.type.clazz} mismatched with requested class=${clazz}"
}
when (appSetting.type) {
AppSettingType.CHAR -> appSetting.value.first()
AppSettingType.STRING -> appSetting.value
AppSettingType.INT -> appSetting.value.toInt()
} as T
}
}
the as T is important to cast the values into the required return type - this is unchecked but the when() clause should be creating the correct types.
Now let's test it:
val c1: Char? = getAppSetting("Char thing", Char::class)
val s1: String? = getAppSetting("String thing", String::class)
val i1: Int? = getAppSetting("Int thing", Int::class)
println("c1=$c1 s1=$s1 i1=$i1")
val c2: Char? = getAppSetting("Char thing")
val s2: String? = getAppSetting("String thing")
val i2: Int? = getAppSetting("Int thing")
println("c2=$c2 s2=$s2 i2=$i2")
}
The output is
c1=C s1=Str i1=42
c2=C s2=Str i2=42
But how do c2/s2/i2 work, the final part is this function
inline fun <reified T : Any> getAppSetting(name: String) = getAppSetting(name, T::class)
This is reified generic parameters... there is no need to pass the clazz because this can be found from the data type of the receiving variable.
There are many articles about this advanced topic, e.g.
https://typealias.com/guides/getting-real-with-reified-type-parameters/
https://medium.com/kotlin-thursdays/introduction-to-kotlin-generics-reified-generic-parameters-7643f53ba513
Now, I didn't completely answer what you wanted because you wanted to receive a DatabaseResult<T> wrapper. What might be possible, is to have a function that returns DatabaseResult<T> and you can obtain the T from it as the "clazz" parameter, but I'll leave that for someone else to improve on :-) but I think that gets you pretty close.

How to use dynamic string substitution in Kotlin?

I'm looking for a Kotlin way to do a dynamic values substitution into a string.
It is clear how to implement it, just want to check if there is something similar in standard library.
Could you help me to find a function which given template and data map returns a resulting string with all template keys replaced with their values?
fun format(template: String, data: Map<String, Any>): String { /* magic */ }
format("${a} ${b} ${a}", mapOf("a" to "Home", "b" to "Sweet)) // -> "Home Sweet Home"
fun format(template: String, data: Map<String, String>): String {
var retval = template
data.forEach { dataEntry ->
retval = retval.replace("\${" + dataEntry.key + "}", dataEntry.value)
}
return retval
}
// The $ signs in the template string need to be escaped to prevent
// string interpolation
format("\${a} \${b} \${a}", mapOf("a" to "Home", "b" to "Sweet"))
Not shorter than lukas.j's answer, just different (using Regex):
val regex = "\\\$\\{([a-z])}".toRegex()
fun format(template: String, data: Map<String, String>) =
regex.findAll(template).fold(template) { result, matchResult ->
val (match, key) = matchResult.groupValues
result.replace(match, data[key] ?: match)
}
I did not find any thing standard to solve the problem.
So here is a balanced (readability/performance/extensibility) solution also handling cases when some substitutions are undefined in dataMap.
makeString("\${a} # \${b} # \${c}", mapOf("a" to 123, "c" to "xyz")) // => "123 # ??? # xyz"
--
object Substitutions {
private val pattern = Pattern.compile("\\$\\{([^}]+)\\}")
fun makeString(
template: String,
dataMap: Map<String, Any?>,
undefinedStub: String = "???"
): String {
val replacer = createReplacer(dataMap, undefinedStub)
val messageParts = splitWithDelimiters(template, pattern, replacer)
return messageParts.joinToString("")
}
private fun createReplacer(dataMap: Map<String, Any?>, stub: String): (Matcher) -> String {
return { m ->
val key = m.group(1)
(dataMap[key] ?: stub).toString()
}
}
private fun splitWithDelimiters(
text: String,
pattern: Pattern,
matchTransform: (Matcher) -> String
): List<String> {
var lastMatch = 0
val items = mutableListOf<String>()
val m = pattern.matcher(text)
while (m.find()) {
items.add(text.substring(lastMatch, m.start()))
items.add(matchTransform(m))
lastMatch = m.end()
}
items.add(text.substring(lastMatch))
return items
}
}

Kotlin Creating List<List<Map<String, String>>>

I am trying to return List<List<Map<String, String>>> from a function in kotlin. I'm new to kotlin.
Edit1
Here's how I am attempting to to this
val a = mutableListOf(mutableListOf(mutableMapOf<String, String>()))
The problem with the above variable is, I am unable to figure out how to insert data into this variable. I tried with this:
val a = mutableListOf(mutableListOf(mutableMapOf<String, String>()))
val b = mutableListOf(mutableMapOf<String, String>())
val c = mutableMapOf<String, String>()
c.put("c", "n")
b.add(c)
a.add(b)
This is giving me:
[[{}], [{}, {c=n}]]
What I want is [[{c=n}]]
Can someone tell me how I can insert data into it?
The end goal I am trying to achieve is to store data in the form of List<List<Map<String, String>>>
EDIT 2
The function for which I am trying to write this dat structure:
fun processReport(file: Scanner): MutableList<List<Map<String, String>>> {
val result = mutableListOf<List<Map<String, String>>>()
val columnNames = file.nextLine().split(",")
while (file.hasNext()) {
val record = mutableListOf<Map<String, String>>()
val rowValues = file.nextLine()
.replace(",(?=[^\"]*\"[^\"]*(?:\"[^\"]*\"[^\"]*)*$)".toRegex(), "")
.split(",")
for (i in rowValues.indices) {
record.add(mapOf(columnNames[i] to rowValues[i]))
print(columnNames[i] + " : " + rowValues[i] + " ")
}
result.add(record)
}
return result
}
You don't need to use mutable data structures. You can define it like this:
fun main() {
val a = listOf(listOf(mapOf("c" to "n")))
println(a)
}
Output:
[[{c=n}]]
If you wanted to use mutable data structures and add the data later, you could do it like this:
fun main() {
val map = mutableMapOf<String, String>()
val innerList = mutableListOf<Map<String, String>>()
val outerList = mutableListOf<List<Map<String, String>>>()
map["c"] = "n"
innerList.add(map)
outerList.add(innerList)
println(outerList)
}
The output is the same, although the lists and maps are mutable.
In response to the 2nd edit. Ah, you're parsing a CSV. You shouldn't try to do that yourself, but you should use a library. Here's an example using Apache Commons CSV
fun processReport(file: File): List<List<Map<String, String>>> {
val parser = CSVParser.parse(file, Charset.defaultCharset(), CSVFormat.DEFAULT.withHeader())
return parser.records.map {
it.toMap().entries.map { (k, v) -> mapOf(k to v) }
}
}
For the following CSV:
foo,bar,baz
a,b,c
1,2,3
It produces:
[[{foo=a}, {bar=b}, {baz=c}], [{foo=1}, {bar=2}, {baz=3}]]
Note that you can simplify it further if you're happy returning a list of maps:
fun processReport(file: File): List<Map<String, String>> {
val parser = CSVParser.parse(file, Charset.defaultCharset(), CSVFormat.DEFAULT.withHeader())
return parser.records.map { it.toMap() }
}
Output:
[{foo=a, bar=b, baz=c}, {foo=1, bar=2, baz=3}]
I'm using Charset.defaultCharset() here, but you should change it to whatever character set the CSV is in.

How to compare two lists item by item in Kotlin

how can i compare "List A" with "List B", and if "list A", has "n" elements that match with "list B" create a "C list" from the "list A" with boolean attached as a new field to every item so if the item was in "list B" it is true else false.
heres a detailed example of what im trying to do:
data class ListAelement(
val fieldA: Double = 2.0,
//this is the field that i have to check with listB
val id: String = "12345",
val somefield: String,
val someotherfield: String
)
data class ListBelement(
//list b only has id
val id: String = "12345"
)
data class ListCelement(
val fieldA: Double = 2.0,
val id: String = "12345",
val somefield: String,
val someotherfield: String,
//this should be true if it is in ListB
val isElementInA: Boolean
)
fun isEqual(listA: List<ListAelement>, listB: List<ListBelement>): List<ListCelement> {
return emptyList()
}
fun main() {
val listA = listOf<ListAelement>(
ListAelement(1.2, "12345"),
ListAelement(1.2, "12343"),
ListAelement(1.2, "1234566"),
ListAelement(1.2, "11233")
)
val listB = listOf<ListBelement>(ListBelement("12345"), ListBelement("123123"))
//new list
val listC = isEqual(listA, listB)
}
Tried following
val listASize = listA.size
val listBSize = listB.size
var counter = 0
while (counter < listASize) {
var id = listA[counter].id
listB.forEach {
if (it.id == id) {
println("matched item $it")
}
}
counter++
}
the accepted response works as it should, heres what i was trying to achieve:
"list A" is an Api response, "list B" is a local database, and list C is to display it and show a check if the item is in the local database
Essentially what you have is a search problem.
I have slightly modified your implementation to make it more accurate for reference:
fun isEqual(listA: List<ListAelement>, listB: List<ListBelement>): List<ListCelement> {
val listC: MutableList<ListCelement> = mutableListOf()
// you don't need a while loop or a counter variable as you need to look at every item in list A
listA.forEach { listAItem ->
var found = false
// search for each item in list A in list B
// this is a linear search
listB.forEach { listBItem ->
if (listBItem.id == listAItem.id) {
// println("matched item $it")
found = true // cache the result that it has been found rather than printing out
}
}
// create your list C with the ListCelement instance that holds the result of whether it was found in List B or not
listC.add(
ListCelement(id=listAItem.id, isElementInA=found)
)
}
return listC
}
You current implementation is of time complexity=O(N * M) (where N and M are the sizes of lists A and B) because for each item in list A, you have to iterate through list B while performing a linear search for it.
This can be optimized through the use of a binary search. Binary search does however require that the search space is sorted. In your case list B being the search space, it needs to be sorted on the id field since that's what you identify elements of list A that are in list B on.
It has a time complexity=O(log N) (where N is the size of the search space) because you take advantage of the order in your search space to discard half the elements at each attempt until you either find your search item or reduce your search space to one item which is not your search item in which case you exit the search.
Below is an implementation using binary search:
/**
* We declare a package-level function main which returns Unit and takes
* an Array of strings as a parameter. Note that semicolons are optional.
*/
data class ListAelement(
val fieldA: Double = 2.0,
// this is the field that i have to check with listB
val id: String,
val someField: String,
val someOtherField: String
)
data class ListBelement(
// list b only has id
val id: String
)
data class ListCelement(
val fieldA: Double = 2.0,
val id: String,
val someField: String,
val someOtherField: String,
// this should be true if it is in ListB
val isElementInA: Boolean
)
fun isEqual(listA: List<ListAelement>, listB: List<ListBelement>): List<ListCelement> {
val sortedListB = listB.sortedWith(compareBy({ it.id }))
val listC: MutableList<ListCelement> = mutableListOf()
listA.forEach { listAItem ->
listC.add(
ListCelement(
id=listAItem.id,
someField=listAItem.someField,
someOtherField=listAItem.someOtherField,
isElementInA=binarySearchListB(sortedListB, listAItem) // linear search replaced with this binary search
)
)
}
return listC
}
// the kotlin collections package ships with a binarySearch() function https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/binary-search.html
// I have opted to implement a custom one so that I can have a Boolean return type
fun binarySearchListB(searchSpace:List<ListBelement>, searchItem: ListAelement, leftIndex:Int = 0, rightIndex: Int = searchSpace.size - 1): Boolean {
val midIndex: Int = (leftIndex + rightIndex) / 2
val midItem: ListBelement = searchSpace[midIndex]
// exit condition if item does not exist
if (leftIndex == rightIndex && searchItem.id != searchSpace[leftIndex].id) {
return false
}
if (midItem.id == searchItem.id) {
// found
return true
} else if(searchItem.id < midItem.id) {
// go to the left
return binarySearchListB(searchSpace, searchItem, leftIndex, midIndex - 1)
} else if(searchItem.id > midItem.id) {
// go to the right
return binarySearchListB(searchSpace, searchItem, midIndex + 1, rightIndex)
}
return false
}
fun main(args: Array<String>) {
val listA = listOf<ListAelement>(
ListAelement(1.2, "12345", "someField", "someOtherField"),
ListAelement(1.2, "12343", "someField", "someOtherField"),
ListAelement(1.2, "1234566", "someField", "someOtherField"),
ListAelement(1.2, "11233", "someField", "someOtherField")
)
val listB = listOf<ListBelement>(ListBelement("1234566"), ListBelement("12345"), ListBelement("123123"))
// new list
val listC = isEqual(listA, listB)
println(listC)
}
Here is the output:
[ListCelement(fieldA=2.0, id=12345, someField=someField, someOtherField=someOtherField, isElementInA=true), ListCelement(fieldA=2.0, id=12343, someField=someField, someOtherField=someOtherField, isElementInA=false), ListCelement(fieldA=2.0, id=1234566, someField=someField, someOtherField=someOtherField, isElementInA=true), ListCelement(fieldA=2.0, id=11233, someField=someField, someOtherField=someOtherField, isElementInA=false)]
UPDATE:
Here's an update that converts list B into a HashSet of strings containing ListBelement ids to yield constant time lookups (i.e O(1)) as suggested in the comments:
/**
* We declare a package-level function main which returns Unit and takes
* an Array of strings as a parameter. Note that semicolons are optional.
*/
data class ListAelement(
val fieldA: Double = 2.0,
// this is the field that i have to check with listB
val id: String,
val someField: String,
val someOtherField: String
)
data class ListBelement(
// list b only has id
val id: String
)
data class ListCelement(
val fieldA: Double = 2.0,
val id: String,
val someField: String,
val someOtherField: String,
// this should be true if it is in ListB
val isElementInA: Boolean
)
fun isEqual(listA: List<ListAelement>, listB: List<ListBelement>): List<ListCelement> {
// construct a HashSet of string ids from listB
// there could be a more idiomatic way to do it
// more about HashSet here -> https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-hash-set/
val listBHashSet: HashSet<String> = hashSetOf()
listB.forEach { listBItem ->
listBHashSet.add(listBItem.id)
}
val listC: MutableList<ListCelement> = mutableListOf()
listA.forEach { listAItem ->
listC.add(
ListCelement(
id=listAItem.id,
someField=listAItem.someField,
someOtherField=listAItem.someOtherField,
isElementInA=listBHashSet.contains(listAItem.id) // linear search replaced with this hashset lookup search
)
)
}
return listC
}
fun main(args: Array<String>) {
val listA = listOf<ListAelement>(
ListAelement(1.2, "12345", "someField", "someOtherField"),
ListAelement(1.2, "12343", "someField", "someOtherField"),
ListAelement(1.2, "1234566", "someField", "someOtherField"),
ListAelement(1.2, "11233", "someField", "someOtherField")
)
val listB = listOf<ListBelement>(ListBelement("1234566"), ListBelement("12345"), ListBelement("123123"))
// new list
val listC = isEqual(listA, listB)
println(listC)
}
The output:
[ListCelement(fieldA=2.0, id=12345, someField=someField, someOtherField=someOtherField, isElementInA=true), ListCelement(fieldA=2.0, id=12343, someField=someField, someOtherField=someOtherField, isElementInA=false), ListCelement(fieldA=2.0, id=1234566, someField=someField, someOtherField=someOtherField, isElementInA=true), ListCelement(fieldA=2.0, id=11233, someField=someField, someOtherField=someOtherField, isElementInA=false)]

Kotlin replace multiple words in string

How to replace many parts of a string with something else in Kotlin using .replace()
For example, we can do it only by replacing one word
fun main(args: Array<String>) {
var w_text = "welcome his name is John"
println("${w_text.replace("his","here")}")
}
and the result will be " welcome here name is John " .
finally we need the result be " welcome here name is alles "
by replacing his to here and john to alles using .replace()
You can do it using multiple consecutive calls to replace():
w_text.replace("his", "here").replace("john", "alles")
You could write an extension that overloads String::replace:
fun String.replace(vararg replacements: Pair<String, String>): String {
var result = this
replacements.forEach { (l, r) -> result = result.replace(l, r) }
return result
}
fun main(args: Array<String>) {
val sentence = "welcome his name is John"
sentence.replace("his" to "here", "John" to "alles")
}
If you have many of those replacement rules, then create a mapping of them and call the replace method in a loop:
val map = mapOf("his" to "here", "john" to "alles", ...)
val sentence = "welcome his name is John"
var result = sentence
map.forEach { t, u -> result = result.replace(t, u) }
println(result)
For the ones interested in replacing a map of values in a text:
private fun replaceText(text: String, keys: Map<String, String>): String =
val replaced = map.entries.fold(text) { acc, (key, value) -> acc.replace(key, value) }
Here is a one liner:
fun String.replace(vararg pairs: Pair<String, String>): String =
pairs.fold(this) { acc, (old, new) -> acc.replace(old, new, ignoreCase = true) }
Test:
#Test fun rep() {
val input = "welcome his name is John"
val output = input.replace("his" to "her", "john" to "alles")
println(output)
output shouldBeEqualTo "welcome her name is alles"
}
Similar to other responses but using Kotlin extension and overloading String::replace to accept a map of oldValue to newValue.
fun String.replace(mapping: Map<String, String>): String {
var str = this
mapping.forEach { str = str.replace(it.key, it.value) }
return str
}
Usage:
val mapping = mapOf("his" to "here", "John" to "alles")
"his dad is John".replace(mapping) // here dad is alles
The issue with just using replace without any regex is:
Let's say I want to replace the occurrence of "here" with "there" inside the string "Where is my bag? Your bag is here." As you can imagine the result will be "Wthere is my bag? Your bag is there." which will not be correct. The solution is to use a regex like given below.
var str = "Where is my bag? Your bag is here."
val replacements = setOf("\\bhere\\b" to "there",
"\\bjohn\\b" to "alles")
replacements.forEach {
str = str.replace(Regex(it.first), it.second)
}