Show or hide decimals - kotlin

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"

Related

Looking for a more idiomatic way to do conditional logging during a list map

There's gotta be a more Kotlin-esque and terse way to do this in Kotlin, but it's not coming to me. Imagine you're doing a mapNotNull operation. Items which cannot be mapped are converted to null to be filtered out. Items that cannot be mapped also result in a warning being printed. This code works, but it's really verbose. Can you help me trim it down?
val listOfStrings = listOf("1","2","3","not an int", "4","5")
val convertedToInts = listOfStrings.mapNotNull {
val converted = it.toIntOrNull()
if(converted == null){
println("warning, cannot convert '$it' to an int")
}
converted
}
I think your code is idiomatic and readable as it is. I prefer to write it with the explicit null-check.
But if you really want to make a shorter one-liner, you could do something like below. But it looks very hacky with the null.apply {} which is needed to return null instead of Unit from the right side of the elvis-operator:
val listOfStrings = listOf("1","2","3","not an int", "4","5")
val convertedToInts: List<Int> = listOfStrings.mapNotNull {
it.toIntOrNull()
?: null.apply { println("warning, cannot convert '$it' to an int") }
}
You could also use run which looks a bit more readable:
?: run {
println("warning, cannot convert '$it' to an int")
null
}

How do I reduce the digits of an incoming multi-digit decimal number from API in Kotlin?

I'm getting a price value from an API but it's a multi-digits decimal number like 0.4785835398457. I want to reduce this number to 3 or 4 digits number like 0.3234 and I'm showing that value in a TextView. So First, I have to form this value and second I need to convert it to String. I tried that DecimalFormat method like at onBindViewHolder part of my RecyclerAdapter.
override fun onBindViewHolder(holder: CoinListViewHolder, position: Int) {
val df = DecimalFormat("#.###")
df.roundingMode= RoundingMode.CEILING //<-----Here
df.format(coinList[position].price_usd.also { holder.itemView.coinPrice.text = it.toString() }) // <----- And here
holder.itemView.coinTicker.text= coinList[position].asset_id
holder.itemView.setOnClickListener {
listener.onItemClick(coinList, position)
}
But it did not work. Please help me.
Thanks in advance.
You can just use a string formatter that uses the number of decimal places you want:
val number = 123.12345
"%.3f".format(number).run(::println)
>> 123.123
That basically converts a float value (f) to a string, to three significant digits (.3). The format spec is here but it's a bit complex.
As far as your code goes, this:
df.format(coinList[position].price_usd.also { holder.itemView.coinPrice.text = it.toString() })
is equivalent to this:
val price = coinList[position].price_usd
holder.itemView.coinPrice.text = price.toString()
df.format(price)
I'm assuming you want to format the price and then display it in the TextView (right now you're just formatting it and doing nothing with the result), which would be this:
df.format(coinList[position].price_usd)
.let { holder.itemView.coinPrice.text = it.toString() }
i.e. do the format, and then do this with the result
Try holder.itemView.coinPrice.text = df.format(coinList[position].price_usd)

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

Add commas or point every 3 digits using kotlin

I want to add commas or point every 3 digit in EditText input.
Example :
input : 1000. Output : 1.000
input : 11000. Output : 11.000
If you are on the JVM you can use
"%,d".format(input)
which gives 11,000 for input 11000. Replace , with any delimiter you require.
If you want to use predefined number formats, e.g. for the current locale, use:
java.text.NumberFormat.getIntegerInstance().format(input);
Be also sure to check the other format instances, e.g. getCurrencyInstance or getPercentInstance. Note that you can use NumberFormat also with other locales. Just pass them to the get*Instance-method.
Some of the second variant can also be found here: Converting Integer to String with comma for thousands
If you are using it via Javascript you may be interested in: How do I format numbers using JavaScript?
Based on Splitframe answer above, did a simplified version (without the var):
fun Int.formatDecimalSeparator(): String {
return toString()
.reversed()
.chunked(3)
.joinToString(",")
.reversed()
}
And added some tests:
#Test
fun whenFormatDecimal_thenReturnFormatted() {
mapOf(
1 to "1",
12 to "12",
123 to "123",
1234 to "1,234",
12345 to "12,345",
123456 to "123,456",
1234567 to "1,234,567",
12345678 to "12,345,678",
123456789 to "123,456,789",
1234567890 to "1,234,567,890",
).forEach { (test, expected) ->
val result = test.formatDecimalSeparator()
assertEquals(expected, result)
}
}
In my case is a KMM project, and we don't support other languages, so it does the job. A better solution I would say to create an expect Util class and each platform implement the formatter taking account of the user Locale, etc.
System.out.println(NumberFormat.getNumberInstance(Locale.US).format(35634646));
This is a simple way that able you to replace default separator with any characters:
val myNumber = NumberFormat.getNumberInstance(Locale.US)
.format(123456789)
.replace(",", "،")
I used this for a non JVM Kotlin environment:
fun formatDecimalSeperators(number :String) :String {
var index = 1
return number
.takeIf { it.length > 3 }
?.reversed()
?.map { if (index++ % 3 == 0) "$it," else it }
?.joinToString("")
?.reversed()
?.removePrefix(",")
?: number
}
You can also use #Roland answer in Android String Resources to format it:
<string name="answer_count">%,01d answers</string>
For a method without getting Locale, you can use an extension to convert your Int into a formatted String like this below :
fun Int.formatWithThousandComma(): String {
val result = StringBuilder()
val size = this.toString().length
return if (size > 3) {
for (i in size - 1 downTo 0) {
result.insert(0, this.toString()[i])
if ((i != size - 1) && i != 0 && (size - i) % 3 == 0)
result.insert(0, "\'")
}
result.toString()
} else
this.toString()
}

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

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
}
}