I have a model that contains some field. I wanna sort this model before a show in recycler by sorted title. my title contains number into a string in some part like session 1 - episode 22. I use this algorithm for sort number in string type with two-digits like episode 22.
sortData.toObservable()
.sorted { o1, o2 ->
val pattern = Pattern.compile("\\d+")
val matcher = pattern.matcher(o1.title)
val matcher2 = pattern.matcher(o2.title)
if (matcher.find()) {
val isFind = matcher2.find()
val o1Num = matcher.group(0).toInt()
val o2Num = if (isFind) {
matcher2.group(0).toInt()
} else {
o1Num + 1
}
return#sorted o1Num - o2Num
} else {
return#sorted o1.title?.compareTo(o2.title ?: "") ?: 0
}
}
.toList()
.subscribeBy(
onError = {
it
},
onSuccess = {
sortData = it
}
)
my problem with this algorithm is when my title contains just simple format like episode 22 and just digits are between 0-99 it's work good but when I have a title that in this format: session 1 - episode 22 and digits are between 0-999 this algorithm won't work and I haven't any sort in my recycler. may please help me for fix this?
I found the solution :)
I must replace the first part with whitespace. then we can get the second part number from string and convert it to int and sort it.
val matcher = pattern.matcher(o1.title?.replace("session 1", ""))
val matcher2 = pattern.matcher(o2.title?.replace("session 1", ""))
Related
I'm working on creating a way to input an amount and format it from left to right with placeholder zeros.
For example, pressing 1 and 2 would show $0.12 pressing 3 would give $1.23. Pressing backspace would give $0.12.
Instead, I am getting $1,0002.00
binding.keypad.btnBackspace.setOnClickListener {
val text = binding.tvTotalValue.text.toString()
if(text.isNotEmpty()) {
binding.tvTotalValue.text = text.drop(1)
}
binding.tvTotalValue.text = ""
}
binding.keypad.onNumberSelected = {
processNewAmount(it.toString())
}
private fun processNewAmount(newValue: String) {
val text = binding.tvTotalValue.text.toString().plus(newValue)
val result = text.replace("[^0-9]".toRegex(), "") // remove any characters
val amount = result.toDouble()
binding.tvTotalValue.text = NumberFormat.getCurrencyInstance().format(amount)
}
What am I doing wrong?
Is there a better way to do this?
I advise keeping a property that stores the entered value without formatting. Each time a number is added, you can add it to this entered number and then format it for the screen. That will be a lot simpler than trying to move/remove existing symbols around in the String.
private var enteredNumber = ""
//...
binding.keypad.btnBackspace.setOnClickListener {
enteredNumber = enteredNumber.dropLast(1)
refreshTotal()
}
binding.keypad.onNumberSelected = {
if(!(it == 0 && enteredNumber == "0")) { // avoid multiple zeros or backspace will act unexpectedly
enteredNumber += it.toString()
refreshTotal()
}
}
//...
private fun refreshTotal() {
val amount = enteredNumber.toDouble() / 100.0
binding.tvTotalValue.text =
NumberFormat.getCurrencyInstance().format(amount)
}
How can we split a text between two char in Kotlin?
Example string:
base_id:94, user_id: 320903, is_Active=1
I want to get only user_id so "320903". But I couldn't do that.
One way to get it is using regex and you can customize it to cover base_id and is_Active
val pattern = Pattern.compile("user_id: (?<id>[0-9]+)")
val matcher = pattern.matcher(text)
if (matcher.find()) {
val group = matcher.group("id").trim()
println(group)
}
The output will be : 320903
Or you can do that with split only and you will get the same result
val items = text.split(",")
val userId = items[1].split(":")[1].trim()
println(userId)
That will work correctly with your example but make sure but for other cases, you may need to customize it or give us many examples to cover them
You can handle the 3 values with one function that support optional whitespace and : or =
fun getValueByTagName(text : String, tag : String) : String {
val pattern = Pattern.compile("$tag[:=][ ]*(?<id>[0-9]+)")
val matcher = pattern.matcher(text)
return if (matcher.find())
matcher.group("id").trim()
else ""
}
To use it
println(getValueByTagName(text, "base_id")) // 94
println(getValueByTagName(text, "user_id")) // 320903
println(getValueByTagName(text, "is_Active")) // 1
Another solution:
Method 1: If your string has exactly the same format that you have shown in the example.
val indexOfUserId = s.indexOf("user_id") // find index of the substring "user_id"
val end = s.indexOf(',', indexOfUserId) // find index of ',' after user_id
val userId s.substring(indexOfUserId + 9, end) // take the substring assuming that userId starts exactly 9 characters after the "u" in "user_id"
Method 2: If your format can vary (in spaces and symbols). Also assuming that user_id is always a number.
val indexOfUserId = s.indexOf("user_id")
val start = s.findAnyOf(List(10) { "$it" }, indexOfUserId)!!.first // find the first digit after "user_id"
val userId = s.substring(start).takeWhile { it.isDigit() } // start from the first digit and continue as long as you are getting digits
Here, List(10) { "$it" } is just a list of all digits in string format and findAnyOf:
Finds the first occurrence of any of the specified [strings] in this char sequence, starting from the specified [startIndex]
Try it yourself
How to enable a button if regex expression is valid for number ,
here is my code in ViewModel
private val changeButtonAvailable = MutableLiveData(false)
fun changeNumbers() {
val regex = "(\\+7|8)[0-9]{10}".toRegex()
if (regex.pattern.length == NUMBER_LENGTH){
changeButtonAvailable.value = true
}
changeButtonAvailable.value = true
}
fun formatNumber(mobile: String): String {
val regex = "(\\+7|8)[0-9]{10}"
val pattern = Pattern.compile(regex)
val matcher = pattern.matcher(mobile)
return if (matcher.matches()) {
val firstNumber = matcher.group(1)
changeButtonAvailable.value = firstNumber?.length == NUMBER_LENGTH
"$firstNumber"
} else {
mobile
}
}
I want make Request +7999999999 not like +7 999 999 999 99
how to make Enable button ? above methods not working for me. thank advance
You should use this regex instead "+7[0-9]{9}". The prefix number is excluded from the count that you mention. Removed the extra slash and 8 length option. You can find appropriate screenshot attached here that verifies this.
You can use this website for testing regex. https://regex101.com/
Suppose I have Double number which I want to convert to String.
I want to have an option which based on that I would have String number without trailing zeroes.
So for example:
option with trailing zeros 123.00 -> "123,00", 123.324 -> "123,32"
option without trailing zeroes 123.00 -> "123", 123.324 -> "123,32"
Is there a nice way to do this in Kotlin?
This is code which I have which I feel is rather ugly:
private const val VALUE_DIVIDER = 100
private const val DIGITS_AMOUNT = 2
private val defaultLocale = Locale("us")
private val currency = "$"
private val cents = 10000
fun print(withoutTrailingZeros: Boolean = true): String {
val price = (cents.toDouble() / VALUE_DIVIDER)
.valueToString()
.let { if (withoutTrailingZeros) it.removeSuffix(",00") else it }
return "$price $currency"
}
private fun Double.valueToString() = round(DIGITS_AMOUNT).replace(".", ",")
private fun Double.round(digits: Int): String =
NumberFormat.getInstance(defaultLocale).apply {
maximumFractionDigits = digits
minimumFractionDigits = digits
isGroupingUsed = false
}.format(this)
UPDATE: The solution provided by #Roma Pochanin works partially, but strangely only as jUnit tests.
After I am running integration tests on Android emulator using this logic this is not working for 0 (it is formatted as "0,00" even when the withoutTrailingZeros flag is true). I heard about some bug related to that Why does new BigDecimal("0.0").stripTrailingZeros() have a scale of 1?
but how it is connected with my case? Can anyone explain?
Please, see the exact sessions from debugger:
working, as jUnit tests: https://ibb.co/HN9n41T
bug, when running instrumentation tests on Android emulator: https://ibb.co/VCrmrMh
There is no function for that in the Kotlin standard library, but you can specify the number of decimal places and the decimal format symbol using Java's DecimalFormat:
val formatSymbols = DecimalFormatSymbols.getInstance().apply {
decimalSeparator = ','
}
val twoDecimalDigitsFormat = DecimalFormat("#.##").apply {
decimalFormatSymbols = formatSymbols
}
val twoTrailingZerosFormat = DecimalFormat("#.00").apply {
decimalFormatSymbols = formatSymbols
}
fun formatPrice(price: Double, withDecimalZeros: Boolean) = if (withDecimalZeros) {
twoTrailingZerosFormat
} else {
// Is number still the same after discarding places?
if (price.toInt().toDouble() == price) {
twoDecimalDigitsFormat
} else {
twoTrailingZerosFormat
}
}.format(price)
println(formatPrice(123.00, true)) // 123,00
println(formatPrice(123.324, true)) // 132,32
println(formatPrice(123.00, false)) // 123
println(formatPrice(123.324, false)) // 123,32
Why don't you use BigDecimal? It's like the default way to deal with prices and similar stuff. You also can consider using BigDecimal's method stripTrailingZeros:
private const val VALUE_DIVIDER = 100
private const val DIGITS_AMOUNT = 2
private val currency = "$"
private val cents = 1298379
fun formatPrice(withoutDecimalZeros: Boolean = true) =
BigDecimal(cents)
.divide(BigDecimal(VALUE_DIVIDER), DIGITS_AMOUNT, RoundingMode.UP)
.let { if (withoutDecimalZeros) it.stripTrailingZeros() else it }
.toString().replace(".", ",")
.let { "$it $currency" }
In Kotlin how to parse a double or float number from string like this:
var st: String? = "90 min"
tried to use toDoubleOrNull but always returns 0 or null.
If you are certain that the number is always at the start, then use split() with space as delimiter and from the returned list take the 1st item and parse it to Double:
val value = st!!.split(" ")[0].toDoubleOrNull()
If there is a case of spaces at the start or in between, use this:
val value = st!!.trim().split("\\s+".toRegex())[0].toDoubleOrNull()
And another way with substringBefore():
val value = st!!.trim().substringBefore(" ").toDoubleOrNull()
Or if there is only 1 integer number in the string, remove every non numeric char with replace():
val value = st!!.replace("\\D".toRegex(), "").toDoubleOrNull()
You can try (assuming you have only one sequence of numbers in your string).
Otherwise, check other answers
val inputString = "123. Test"
val regex = "\\d+(\\.\\d+)?".toRegex()
val match = regex.find(inputString)
if (match != null) {
println("Result: " + match.value)
} else {
println("Result: Not found")
}
This should work...
val pattern = Pattern.compile("\\d+") // the regex
val matcher = pattern.matcher("hello123hgj") // your string
val doubles = ArrayList<Double>() // results
while (matcher.find())
{ // for each match
doubles.add(Double.parseDouble(matcher.group())) // convert to double
}
It depends on what you need. If you just have to get 90 and the string is formatted always in the same way you can just
var string = "90 min"
var floatFromString = string.substring(0,2).toFloat()
but it's better to learn how to use LocalDate and DateTimeFormatter