Make string starts with specified prefix in Kotlin - kotlin

I am trying to find out the native way to concatenate prefix to string, but only in case, it was not.
This code checks the text variable and makes it start with "#".
val text = "123456"
val prefix = "#"
val textFormatted = (if (text.startsWith(prefix)) "" else prefix ) + text
I hope there are clean solutions somewhere in Kotlin

An alternative would be to use removePrefix:
val textFormatted = prefix + text.removePrefix(prefix)
Otherwise you could also keep the if but write it the following way to avoid extra parentheses and extra concatenation, and also make the code closer to the semantics:
val textFormatted = if (text.startsWith(prefix)) text else "$prefix$text"
But your solution works too.

You can use the string interpolation from the kotlin, example:
val text:String = "123456#123456"
val prefix:String = "#"
val interpolation:String = "#${text.removePrefix(prefix)}"

Related

How to replace string characters that are not in a reference list in kotlin

I have a reference string on which the allowed characters are listed. Then I also have input strings, from which not allowed characters should be replaced with a fixed character, in this example "0".
I can use filter but it removes the characters altogether, does not offer a replacement. Please note that it is not about being alphanumerical, there are ALLOWED non-alphanumerical characters and there are not allowed alphanumerical characters, referenceStr happens to be arbitrary.
var referenceStr = "abcdefg"
var inputStr = "abcqwyzt"
inputStr = inputStr.filter{it in referenceStr}
This yields:
"abc"
But I need:
"abc00000"
I also considered replace but it looks more like when you have a complete reference list of characters that are NOT allowed. My case is the other way around.
Given:
val referenceStr = "abcd][efg"
val replacementChar = '0'
val inputStr = "abcqwyzt[]"
You can do this with a regex [^<referenceStr>], where <referenceStr> should be replaced with referenceStr:
val result = inputStr.replace("[^${Regex.escape(referenceStr)}]".toRegex(), replacementChar.toString())
println(result)
Note that Regex.escape is used to make sure that the characters in referenceStr are all interpreted literally.
Alternatively, use map:
val result = inputStr.map {
if (it !in referenceStr) replacementChar else it
}.joinToString(separator = "")
In the lambda decide whether the current char "it" should be transformed to replacementChar, or itself. map creates a List<Char>, so you need to use joinToString to make the result a String again.

Building string from list of list of strings

I rather have this ugly way of building a string from a list as:
val input = listOf("[A,B]", "[C,D]")
val builder = StringBuilder()
builder.append("Serialized('IDs((")
for (pt in input) {
builder.append(pt[0] + " " + pt[1])
builder.append(", ")
}
builder.append("))')")
The problem is that it adds a comma after the last element and if I want to avoid that I need to add another if check in the loop for the last element.
I wonder if there is a more concise way of doing this in kotlin?
EDIT
End result should be something like:
Serialized('IDs((A B,C D))')
In Kotlin you can use joinToString for this kind of use case (it deals with inserting the separator only between elements).
It is very versatile because it allows to specify a transform function for each element (in addition to the more classic separator, prefix, postfix). This makes it equivalent to mapping all elements to strings and then joining them together, but in one single call.
If input really is a List<List<String>> like you mention in the title and you assume in your loop, you can use:
input.joinToString(
prefix = "Serialized('IDs((",
postfix = "))')",
separator = ", ",
) { (x, y) -> "$x $y" }
Note that the syntax with (x, y) is a destructuring syntax that automatically gets the first and second element of the lists inside your list (parentheses are important).
If your input is in fact a List<String> as in listOf("[A,B]", "[C,D]") that you wrote at the top of your code, you can instead use:
input.joinToString(
prefix = "Serialized('IDs((",
postfix = "))')",
separator = ", ",
) { it.removeSurrounding("[", "]").replace(",", " ") }
val input = listOf("[A,B]", "[C,D]")
val result =
"Serialized('IDs((" +
input.joinToString(",") { it.removeSurrounding("[", "]").replace(",", " ") } +
"))')"
println(result) // Output: Serialized('IDs((A B,C D))')
Kotlin provides an extension function [joinToString][1] (in Iterable) for this type of purpose.
input.joinToString(",", "Serialized('IDs((", "))')")
This will correctly add the separator.

Kotlin String substitution not working when string is read from file

I have written a code that reads a text file. The text files contain placeholders which I would like to replace. The substitution does not work this way and the string is printed with the placeholders. Here is the code that I have written for this:
class TestSub(val sub: Sub) {
fun create() = template()
fun template() = Files.newBufferedReader(ClassPathResource(templateId.location).file.toPath()).readText()
}
data class Sub(val name: String, val age: Int)
Here is the main function that tries to print the final string:
fun main(args: Array<String>) {
val sub = Sub("Prashant", 32)
println(TestSub(sub).create())
}
However, when, instead of reading a file, I use a String, the following code works (Replacing fun template())
fun template() = "<h1>Hello ${sub.name}. Your age is ${sub.age}</h1>"
Is there a way to make string Substitution work when reading the content of a file?
Kotlin does not support String templates from files. I.e. code like "some variable: $variable" gets compiled to "some variable: " + variable. String templates are handled at compile time, which means it does not work with text loaded from files, or if you do something else to get the String escaped into a raw form. Either way, it would, as danielspaniol mentioned, be a security threat.
That leaves three options:
String.format(str)
MessageFormat.format(str)
Creating a custom engine
I don't know what your file contains, but if it's the String you used in the template function, change it to:
<h1>Hello {0}. Your age is {1,integer}</h1>
This is for MessageFormat, which is my personal preference. If you use String.format, use %s instead, and the other appropriate formats.
Now, use that in MessageFormat.format:
val result = MessageFormat.format(theString, name, age);
Note that if you use MessageFormat, you'll need to escape ' as ''. See this.
String substitution using ${...} is part of the string literals syntax and works roughly like this
val a = 1
val b = "abc ${a} def" // gets translated to something like val b = "abc " + a + " def"
So there is no way for this to work when you load from a text file. This would also be a huge security risk as it would allow for arbitrary code execution.
However I assume that Kotlin has something like a sprintf function where you can have placeholders like %s in your string and you can replace them with values
Take a look here. It looks like the easiest way is to use String.format
You are looking for something similar to Kotlin String templates for raw Strings, where placeholders like $var or ${var} are substituted by values, but this functionality needs to be available at runtime (for text read from files).
Methods like String.format(str) or MessageFormat.format(str) use other formats than the notation with the dollar prefix of Kotlin String templates. For "Kotlin-like" placeholder substitution you could use the function below (which I developed for similar reasons). It supports placeholders as $var or ${var} as well as dollar escaping by ${'$'}
/**
* Returns a String in which placeholders (e.g. $var or ${var}) are replaced by the specified values.
* This function can be used for resolving templates at RUNTIME (e.g. for templates read from files).
*
* Example:
* "\$var1\${var2}".resolve(mapOf("var1" to "VAL1", "var2" to "VAL2"))
* returns VAL1VAL2
*/
fun String.resolve(values: Map<String, String>): String {
val result = StringBuilder()
val matcherSimple = "\\$([a-zA-Z_][a-zA-Z_0-9]*)" // simple placeholder e.g. $var
val matcherWithBraces = "\\$\\{([a-zA-Z_][a-zA-Z_0-9]*)}" // placeholder within braces e.g. ${var}
// match a placeholder (like $var or ${var}) or ${'$'} (escaped dollar)
val allMatches = Regex("$matcherSimple|$matcherWithBraces|\\\$\\{'(\\\$)'}").findAll(this)
var position = 0
allMatches.forEach {
val range = it.range
val placeholder = this.substring(range)
val variableName = it.groups.filterNotNull()[1].value
val newText =
if ("\${'\$'}" == placeholder) "$"
else values[variableName] ?: throw IllegalArgumentException("Could not resolve placeholder $placeholder")
result.append(this.substring(position, range.start)).append(newText)
position = range.last + 1
}
result.append(this.substring(position))
return result.toString()
}
String templates only work for compile-time Sting literals, while what u read from a file is generated at runtime.
What u need is a template engine, which can render templates with variables or models at runtime.
For simple cases, String.format or MessageFormat.format in Java would work.
And for complex cases, check thymeleaf, velocity and so on.

How to convert Object(with value) into Map

I have a object that I want to print it into string [key1=value1&key2=value2...etc] without the null value key value pair and comma into &.
So first of all i think of putting it into a map but it won't work and I don know how it work either.
val wxPayOrderObj = WxPayOrder(appid = "wx0b6dcsad20b379f1", mch_id =
"1508334851", nonce_str = UUID.randomUUID().toString(),sign = null,
body = "QQTopUp", out_trade_no = "20150806125346", total_fee = req.total_fee,
spbill_create_ip = "123.12.12.123",
trade_type = "JSAPI", openid = "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o")
so the output will be
appid=wx0b6dc78d20b379f1&mch_id=150788851&nonce_str=UUID.randomUUID().toString()&
body=QQTopUp&out_trade_no=20150806125346&total_fee=req.total_fee&
spbill_create_ip=123.12.12.123&trade_type=JSAPI&openid=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
anyone please help me, thanks in advances.
I don't really get your question, but you want to convert object to string (to a format that you want)?
Override the object's toString() to return "[key1=value1&key2=value2...etc]"
example
override fun toString(){
// make sure you compute the data first
val answer = "[key1=$value1&key2=$value2...etc]"
return answer
}
The $ is used in string templates (That's directly writing the name of a variable, the value will be used later to be concatenated) with other strings)

Escape ${something} in a Kotlin String

What is the correct way of defining a Kotlin string that includes the characters for declaring a template substitution, but not have this evaluated as a template?
For example: "${something}" just treated as an ordinary string.
I would like to use the Spring value annotation:
#Value("${some.property}) lateinit var foobar : String?
This works for me:
val s = "\${foo}"
println("s = ${s}") // prints s = ${foo}
The documented way also works fine:
val s = "${'$'}{foo}"
println("s = ${s}") // prints s = ${foo}