NumberFormat to Number in Kotlin - kotlin

I really want to know how to make the inverse process of this
fun makeAFormattedString(currencyCode: String, numberToConvert: Float): String{
val format: NumberFormat = NumberFormat.getCurrencyInstance()
format.maximumFractionDigits = 2
format.currency = Currency.getInstance(currencyCode)
return format.format(numberToConvert)
}
when I type: ("MXN",3000) my result is
"MX $3000.00"
I want to know how to get the number (3000) with this string
"MX $3000.00"

"MX $3000.00".takeLastWhile { it != '$' }
is one of many ways.
val result = "[^\\d]+?(\\d+\\.?\\d*)".toRegex().find("MX $3000.00")
result?.groupValues?.last()
works too.
The problem is there's no way to tell whether you started with an Int or a Float. If you started with 3000, it formats as 3000.00 and if you started with 3000.45, it formats as 3000.45. The result will be a Float, and you can choose what to do with it.

Related

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 - Type mismatch: inferred type is Any? but Boolean was expected

I'm trying my hands on Kotlin. Being from a Python background is really giving me a tough time to get the knack of the Kotlin syntax. I'm trying to do a simple dictionary (Mutable Map) operation. However, its giving me exceptions.
This is what I tried. Kotlin compiler
Adding the code snippet for reference.
fun main() {
val openActivityMap = mutableMapOf<String, MutableMap<String, Any>>()
val packageName = "amazon"
val currentTime = 23454321234
if(openActivityMap.containsKey(packageName)){
if(openActivityMap[packageName]?.get("isAlreadyApplied")){
if((openActivityMap[packageName]?.get("lastAppliedAt") - currentTime) > 3600){
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
else{
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
}
I'm a bit late to the party, but I'd like to point out another solution here.
As I commented on the OP, heterogeneous maps with fixed string keys like this are usually better expressed with classes in Kotlin. For instance, in your case, the class for your main map's values could be the following:
data class PackageInfo(
var isAlreadyApplied: Boolean,
var lastAppliedAt: Long,
)
(you could obviously add more properties if need be)
This would save you all the casts on the final values.
Another point I'd like to make is that if you access the value for a key anyway, you don't need to check up front the existence of the key with containsKey. Maps return null for keys that are not associated with any value (this is why you need to check for null after getting the value).
The compiler cannot see the correlation between containsKey and the subsequent get or [] access. However, it's smart enough to understand a null check if you simply get the value first and then check for null.
This always applies unless you want to tell the difference between keys that aren't in the map and keys that are in the map but associated null values (which is quite rare).
All in all, I would write something like that:
fun main() {
val openActivityMap = mutableMapOf<String, PackageInfo>()
val packageName = "amazon"
val currentTime = 23454321234
val packageInfo = openActivityMap[packageName]
if (packageInfo != null) { // the key was found and the value is smart cast to non-null in the next block
if (packageInfo.isAlreadyApplied) {
if ((packageInfo.lastAppliedAt - currentTime) > 3600) {
packageInfo.isAlreadyApplied = false
}
} else {
packageInfo.isAlreadyApplied = false
}
}
}
data class PackageInfo(
var isAlreadyApplied: Boolean,
var lastAppliedAt: Long,
)
I would recommend writing tests first and working in small increments, but this should fix your compilation issues:
fun main() {
val openActivityMap = mutableMapOf<String, MutableMap<String, Any>>()
val packageName = "amazon"
val currentTime = 23454321234
if(openActivityMap.containsKey(packageName)){
if(openActivityMap[packageName]?.get("isAlreadyApplied") as Boolean){
if((openActivityMap[packageName]?.get("lastAppliedAt") as Long - currentTime) > 3600){
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
else {
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
}
EDIT: Also I prefer to avoid nullable variables and mutable objects in general, but I suppose there's an exception to every rule.
Couldn't you just declare your Map<String, Any> to return a Boolean instead of Any? So,
val openActivityMap = mutableMapOf<String, MutableMap<String, Boolean>>()
It looks like you're trying to use your second Map to store both Booleans and Ints, which is complicating the logic. You'll need to typecast if you decide to approach it without Typing.
There's a problem with the 2 statement below
if(openActivityMap[packageName]?.get("isAlreadyApplied"))
if((openActivityMap[packageName]?.get("lastAppliedAt") - currentTime) > 3600)
As we all know, an IF statement requires a boolean value for it's param. The types of both statement are unknown at compilation time as they are of a Generic type, Any. As such,
openActivityMap[packageName]?.get("isAlreadyApplied") could be a null or of type Any (Not Boolean).
openActivityMap[packageName]?.get("lastAppliedAt") could be a null or of type Any (an Int was expected here for computation).
This would throw compilation errors as the compiler does not know the types to go with. What could be done is to cast to it's proper types.
Solution
openActivityMap[packageName]?.get("isAlreadyApplied") as Boolean ?: false
((openActivityMap[packageName]?.get("lastAppliedAt") as Int ?: 0) - currentTime)
Giving a default value if it's null.
maybe you can try something like this
if (openActivityMap.containsKey(packageName)) {
val packageMap = openActivityMap[packageName]!!
val applyRequired = (packageMap["lastAppliedAt"] as Long - currentTime) > 3600
packageMap["isAlreadyApplied"] = packageMap.containsKey("isAlreadyApplied") && !applyRequired
}
btw. do you really want to have lastAppliedAt to be in te future? otherewise it will never be > 3600

String "2" toInt returns 50

Im just working through some simple practice problems in kotlin. In my code below I'm taking a number and attempting to add the number members together. Ex. 29, return 2 + 9 = 11. There could be a better way to accomplish this but, I'm taking the numbers, converting to string, and then putting them into a list, ie ["2","9"] when I attempt to convert list[0].toInt() it returns 50. It appears there is some rounding taking place but I have not found another kotlin method to work with. Can anyone offer some insights? TIA
fun main(args: Array<String>) {
fun addTwoDigits(n: Int): Int {
val sliced = n.toString().toList()
val int1 = sliced[0].toInt()
println(sliced[0]) //returns "2"
println(int1) // returns 50
return sliced[0].toInt() + sliced[1].toInt()
}
println(addTwoDigits(29))
}
Koltin Char.toString(), which you are using in line:
val int1 = sliced[0].toInt()
converts the character using the ASCII Code table.
You can simply add a toString() call before the toInt() call:
val int1 = sliced[0].toString().toInt()

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