How to use Kotlin to find whether a string is numeric? - kotlin

I'd like to use a when() expression in Kotlin to return different values from a function. The input is a String, but it might be parsable to an Int, so I'd like to return the parsed Int if possible, or a String if it is not. Since the input is a String, I cannot use the is type check expression.
Is there any idiomatic way to achieve that?
My problem is what the when() expression should look like, not about the return type.

Version 1 (using toIntOrNull and when as requested)
fun String.intOrString(): Any {
val v = toIntOrNull()
return when(v) {
null -> this
else -> v
}
}
"4".intOrString() // 4
"x".intOrString() // x
Version 2 (using toIntOrNull and the elvis operator ?:)
when is actually not the optimal way to handle this, I only used when because you explicitly asked for it. This would be more appropriate:
fun String.intOrString() = toIntOrNull() ?: this
Version 3 (using exception handling):
fun String.intOrString() = try { // returns Any
toInt()
} catch(e: NumberFormatException) {
this
}

The toIntOrNull function in the kotlin.text package (in kotlin-stdlib) is probably what you're looking for:
toIntOrNull
fun String.toIntOrNull(): Int? (source)
Platform and version requirements: Kotlin 1.1
Parses the string as an Int number and returns the result or null if the string is not a valid representation of a number.
fun String.toIntOrNull(radix: Int): Int? (source)
Platform and version requirements: Kotlin 1.1
Parses the string as an Int number and returns the result or null if the string is not a valid representation of a number.
More information: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/to-int-or-null.html

Using let for one
fun isInteger(str: String?) = str?.toIntOrNull()?.let { true } ?: false

Simple and intuitive
fun isNumeric(str: String) = str.all { it in '0'..'9' }
As #coolMind point out, if you want to filter +/-
fun isNumeric(str: String): Boolean = str
.removePrefix("-")
.removePrefix("+")
.all { it in '0'..'9' }
The performance would be similar

If you want to check if it is numeric (Int) the string and do something a simple solution could be:
if (myString.toIntOrNull() != null) {
//Write your code you want to execute if myString is (Int)
} else {
//Write your code you want to execute if myString is (not Int)
}

Sharing Regex matches solution, repost from my answer here
Best suited solution if negative and positive number which can be formatted with '-' and '.'
below method returns true if formatted string number matches the regex pattern
fun isPosOrNegNumber(s: String?) : Boolean {
val regex = """^(-)?[0-9]{0,}((\.){1}[0-9]{1,}){0,1}$""".toRegex()
return if (s.isNullOrEmpty()) false
else regex.matches(s)
}
Above sample regex is only for US number formats but if you want to use EU number formats then just replace '.' with ',' in regex pattern string
Note:. if the numbers contain commas then just replace it while sending to this method or better form a regex pattern with commas in it.

I searched for the same and I found this answer so I have made my own version from the above answer:
//function to check strin is int or bull
fun String.intOrString(): Boolean{
val v = toIntOrNull()
return when(v) {
null -> false
else -> true
}
}

Related

Kotlin: check if string is numeric

Is there a simple way to check if user's input is numeric? Using regexes and exceptions seems too complicated here.
fun main {
val scan = Scanner(System.`in`)
val input = scanner.nextLine()
if (!input.isNumeric) {
println("You should enter a number!")
}
}
The method mentioned above will work for a number <= approximately 4*10^18 essentially max limit of Double.
Instead of doing that since String itself is a CharSequence, you can check if all the character belong to a specific range.
val integerChars = '0'..'9'
fun isNumber(input: String): Boolean {
var dotOccurred = 0
return input.all { it in integerChars || it == '.' && dotOccurred++ < 1 }
}
fun isInteger(input: String) = input.all { it in integerChars }
fun main() {
val input = readLine()!!
println("isNumber: ${isNumber(input)}")
println("isInteger: ${isInteger(input)}")
}
Examples:
100234
isNumber: true
isInteger: true
235.22
isNumber: true
isInteger: false
102948012120948129049012849102841209849018
isNumber: true
isInteger: true
a
isNumber: false
isInteger: false
Its efficient as well, there's no memory allocations and returns as soon as any non-satisfying condition is found.
You can also include check for negative numbers by just changing the logic if hyphen is first letter you can apply the condition for subSequence(1, length) skipping the first character.
joining all the useful comments and putting it in a input stream context, you can use this for example:
fun readLn() = readLine()!!
fun readNumericOnly() {
println("Enter a number")
readLn().toDoubleOrNull()?.let { userInputAsDouble ->
println("user input as a Double $userInputAsDouble")
println("user input as an Int ${userInputAsDouble.toInt()}")
} ?: print("Not a number")
}
readNumericOnly()
for input: 10
user input as a Double 10.0
user input as an Int 10
for input: 0.1
user input as a Double 0.1
user input as an Int 0
for input: "word"
Not a number
Simply use : text.isDigitsOnly() in kotlin.
Well all the answers here are best suited for their own scenarios:
But not all string are numeric digits it can have (-) and (.) decimal pointers.
So to accomplish this I made a cocktail of all the answers suggested below and from other posts as well which - looks like below :
fun isPosOrNegNumber(s: String?) : Boolean {
return if (s.isNullOrEmpty()) false
else{
if(s.first()=='-' && s.filter { it == '.' }.count() <= 1) {
s.removeRange(0,1).replace(".","").all{Character.isDigit(it)}
}
else s.all {Character.isDigit(it)}
}
}
Above code does a good job for its purpose.
But then it struck me kotlin does an even better job with matching a regex and voila the solution became simple and elegant as below :
fun isPosOrNegNumber(s: String?) : Boolean {
val regex = """^(-)?[0-9]{0,}((\.){1}[0-9]{1,}){0,1}$""".toRegex()
return if (s.isNullOrEmpty()) false
else regex.matches(s)
}
This sample regex is only for US number formats but if you want to use EU number formats then just replace '.' with ','
Bdw. if the numbers contain commas then just replace it while sending to this method or better form a regex pattern with commas in it.
Another way to check if the given string is numeric( to check for both negative and positive values ) or not:
val intChars = '0'..'9'
fun isNumeric(input: String) = input
.removePrefix("-")
.all { it in '0'..'9' }
A simple answer without any custom functions is to utilise toDoubleOrNull function. If it returns null, the string is not numeric.
val string = "-12345.666"
if (string.toDoubleOrNull()!=null) // string is numeric
{
//do something
}
If you know the input only contains integers you can also use toIntOrNull likewise

Show or hide decimals

I have a double value, if that number is like this: 123.00 I need to show it as 123 only, without decimal places, but, if the number is like 123.23 or 123.2, I need to show it with the present decimal places: 123.23 or 123.2, as the case may be.
I have tried with decimal format but I couldn't find the right pattern.
It is a better way to do this than a string conversion and operate with substrings and things like that?
DecimalFormat is what you're looking for I think:
import java.text.DecimalFormat
fun main(args : Array<String>) {
val df = DecimalFormat("0.##")
println(df.format(123.0))
println(df.format(123.3))
println(df.format(123.32))
println(df.format(123.327))
}
Output:
123
123.3
123.32
123.33
Here's one way you could do it:
fun func(x: Double): String {
if (x.rem(1).compareTo(0) == 0){
return x.toInt().toString();
} else {
return x.toString();
}
}
print(func(1.32132)); //Returns 1.32132
print(func(3.00)); //Returns 3
You could use DecimalFormat with setMaximumFractionDigits. Creating an extension function would keep the complexity away from the call-site:
fun Double.toStringRounded(fracDigits: Int) = DecimalFormat().apply {
setMaximumFractionDigits(fracDigits)
}.format(this)
Usage:
3.14159.toStringRounded(2) // will be "3.14"

How to create String with certain length and same value effectively in Kotlin

I knew this can be achieved with for loop but I am looking for better solution.
createDummyString(1,'A') = 'A'
createDummyString(2.'A') = 'AA'
This will be used in hangman. Thank you.
You can do it like in the example below. To learn more about Strings read this: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html
fun createDummyString(repeat : Int, alpha : Char) = alpha.toString().repeat(repeat)
Addendum:
If you want to make it more kotlinesque, you can also define repeat as extension function on Char
fun Char.repeat(count: Int): String = this.toString().repeat(count)
and call it like this:
'A'.repeat(1)
CharSequence has an extension method for this.
fun CharSequence.repeat(n: Int): String // for any whole number
Example
println("A".repeat(4)) // AAAA
println("A".repeat(0)) // nothing
println("A".repeat(-1)) // Exception
Reference : https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/repeat.html
I created a utility function using infix operator for this :
infix fun Int.times(s : CharSequence): CharSequence{
return s.repeat(this)
}
//Use like val twoAs = 2 times "A"
println(a) // AA

Convert from char to operator Kotlin

Is there any way to convert a char, lets say with a value of '+', into the operator +? Something like this:
println(1 charOperator 1);
output:
2
You can use something like this:
fun operatorFromChar(charOperator: Char):(Int, Int)->Int
{
return when(charOperator)
{
'+'->{a,b->a+b}
'-'->{a,b->a-b}
'/'->{a,b->a/b}
'*'->{a,b->a*b}
else -> throw Exception("That's not a supported operator")
}
}
and later call:
println(operatorFromChar('+').invoke(1,1))
Operators are, at the end of the way, functions. If you return a function with the operator's job, you can invoke it as it was the operator itself, but it will never be as "pretty" as calling the operator directly.
This isn't really possible. Maybe you should add your current solution and there's another way to help you out.
Here's a sneaky solution for calculating expressions with + and - only:
val exp = "10+44-12+3"
val result = exp.replace("-", "+-").split("+").sumBy { it.toInt() }
You can do something like
infix fun Int.`_`(that: Int) = this + that
where the backtick is unnecessary to this character but maybe necessary for other character. Then you can try:
println(2 _ 3) // output: 5
Update according to the comment:
I mean something like
val expr = input.split(' ')
when (expr[1])
{
'+' -> return expr[0].toInt() + expr[2].toInt()
'-' -> return expr[0].toInt() - expr[2].toInt()
'*' -> return expr[0].toInt() * expr[2].toInt()
'/' -> return expr[0].toInt() / expr[2].toInt()
// add more branches
}
However, I was wondering whether there is a better and tricky solution from the grammar of Kotlin.
What you basically want is an Char to result of an operation mapping. So, I decided to return the result right away and not a lambda.
fun Int.doOperation(charOperator: Char, x: Int) = when(charOperator) {
'+' -> this + x
'-' -> this - x
'/' -> this / x
'*' -> this * x
else -> throw IllegalArgumentException("Not supported")
}
Using an extension function maybe (?) makes the syntax a little nicer. You decide.
Call site:
println(5.doOperation('+', 6))
You can use the interpreter class in beanshell library
to convert string text automatically to result
for example
interpreter.eval("equal=2*3")
println(interpreter.get("equal").toString().toDouble().toString())
or can use expression class that does the same thing
fun String.stringToConditionalOperators(): (Boolean, Boolean) -> Boolean {
return when (this.lowercase(Locale.getDefault())) {
"and" -> {
{ a: Boolean, b: Boolean ->
a && b
}
}
"or" -> {
{ a: Boolean, b: Boolean ->
a || b
}
}
// You can add more operator 🤩
else -> {
return { a: Boolean, b: Boolean ->
a || b
}
}
}
}
Usage..
val operator = "AND"
operator.stringToConditionalOperators().invoke(one, two)

Compare String Kotlin

I'm studying kotlin, but I'm very disappointed, I can not compare two Strings.
What is the right way to compare.
btn_login.setOnClickListener {
val login = input_email.text.trim()
val pass = input_password.text.trim()
if( login.equals( pass ) ){
startActivity<MainActivity>()
}
if (login?.equals(other = pass)){
startActivity<MainActivity>()
}
if (login == pass){
startActivity<MainActivity>()
}
}
According to documentation for structual equality use ==. It is translated to a?.equals(b) ?: (b === null).
In you case convert login and pass from SpannableStringBuilder to String.
val login = input_email.text.trim().toString()
Here is the example for matching the two strings using kotlin.
If you are using == (double equals) for matching the string then it's compare the address & return maximum time wrong result as per java documentation so use equals for the same
If you want to use equal ignore case then pass the true in the equals method of String
if (s1.equals(s2,true))
other wise you can just use this without boolean like
if (s1.equals(s2,false)) or if (s1.equals(s2))
compleate code is below
fun main(args: Array<String>) {
val s1 = "abc"
val s2 = "Abc"
if (s1.equals(s2,true))
{
println("Equal")
}
else
{
println("Not Equal")
}
}
Covert both the SpannableStringBuilder to string with toString, this should work.
val login = input_email.text.trim().toString()
val pass = input_password.text.trim().toString()
if (login == pass){
startActivity<MainActivity>()
}
1. == :
if ( string1 == string2 ){...}
2. equals :
Indicates whether some other object is "equal to" this one.
Implementations must fulfil the following requirements:
Reflexive: for any non-null reference value x, x.equals(x) should
return true.
Symmetric: for any non-null reference values x and y, x.equals(y)
should return true if and only if y.equals(x) returns true.
Transitive: for any non-null reference values x, y, and z, if
x.equals(y) returns true and y.equals(z) returns true, then
x.equals(z) should return true
Consistent: for any non-null reference values x and y, multiple
invocations of x.equals(y) consistently return true or consistently
return false, provided no information used in equals comparisons on
the objects is modified.
/** * Returns `true` if this string is equal to [other], optionally ignoring character case. * * #param ignoreCase `true` to ignore character case when comparing strings. By default `false`. */
public fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean
3. compareTo :
public override fun compareTo(other: String): Int
Compares this object with the specified object for order. Returns zero
if this object is equal to the specified other object, a negative
number if it's less than other, or a positive number if it's greater
than other.
public fun String.compareTo(other: String, ignoreCase: Boolean = false): Int
Compares two strings lexicographically, optionally ignoring case
differences
i know this is way too late, but as a newbie learning Kotlin, i had the same doubts.
then i came across this wonderful article that articulates the various string comparison types in Kotlin and the differences between them all.
in short both == and .equals() can be used to compare the value of 2 strings in kotlin.
hopefully that helps
With case checking
String a=.....
String b=.....
if(a==b){
}
IgnoreCase
if(a.equals(b,false))
KOTLIN:
if (editText1.text.toString() == editText2.text.toString() ) {
println("Should work now! The same value")
}
Try the following solution, see if it helps:
val passStr: String = textView.text.toString()
if( loginStr.compareTo(passStr, false) ){
startActivity<MainActivity>()
}
Try this surely will work.
val style = buildString { karthik}
val style2 = buildString { karthik }
var result = style.equals(style2)
if(result){//Do something}