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)
Related
I am playing with kotlin language and I tried the following code:
data class D( val n: Int, val s: String )
val lst = listOf( D(1,"one"), D(2, "two" ) )
val res = lst.reduce { acc:String, d:D -> acc + ", " + d.toString() }
The last statement causes the following errors:
Expected parameter of type String
Expected parameter of type String
Type mismatch: inferred type is D but String was expected
while this version of the last statement works:
val res = lst.map { e -> e.toString() }.reduce { acc, el -> acc + ", " + el }
I do not understand why the first version does not work. The formal definition of the reduce function, found here, is the following:
inline fun <S, T : S> Iterable<T>.reduce(
operation: (acc: S, T) -> S
): S
But this seems in contrast with the following sentence, on the same page:
Accumulates value starting with the first element and applying
operation from left to right to current accumulator value and each
element.
That is, as explained here:
The difference between the two functions is that fold() takes an
initial value and uses it as the accumulated value on the first step,
whereas the first step of reduce() uses the first and the second
elements as operation arguments on the first step.
But, to be able to apply the operation on first and second element, and so on, it seems to me tha the operation shall have both arguments of the base type of the Iterable.
So, what am I missing ?
Reduce is not the right tool here. The best function in this case is joinToString:
listOf(D(1, "one"), D(2, "two"))
.joinToString(", ")
.let { println(it) }
This prints:
D(n=1, s=one), D(n=2, s=two)
reduce is not designed for converting types, it's designed for reducing a collection of elements to a single element of the same type. You don't want to reduce to a single D, you want a string. You could try implementing it with fold, which is like reduce but takes an initial element you want to fold into:
listOf(D(1, "one"), D(2, "two"))
.fold("") { acc, d -> "$acc, $d" }
.let { println(it) }
However, this will add an extra comma:
, D(n=1, s=one), D(n=2, s=two)
Which is exactly why joinToString exists.
You can see the definition to understand why its not working
To make it work, you can simply create an extension function:
fun List<D>.reduce(operation: (acc: String, D) -> String): String {
if (isEmpty())
throw UnsupportedOperationException("Empty list can't be reduced.")
var accumulator = this[0].toString()
for (index in 1..lastIndex) {
accumulator = operation(accumulator, this[index])
}
return accumulator
}
you can use it as:
val res = lst.reduce { acc:String, d:D -> acc + ", " + d.toString() }
or simply:
val res = lst.reduce { acc, d -> "$acc, $d" }
You can modify the function to be more generic if you want to.
TL;DR
Your code acc:String is already a false statement inside this line:
val res = lst.reduce { acc:String, d:D -> acc + ", " + d.toString() }
Because acc can only be D, never a String! Reduce returns the same type as the Iterable it is performed on and lst is Iterable<D>.
Explanation
You already looked up the definition of reduce
inline fun <S, T : S> Iterable<T>.reduce(
operation: (acc: S, T) -> S
): S
so lets try to put your code inside:
lst is of type List<D>
since List extends Iterable, we can write lst : Iterable<D>
reduce will look like this now:
inline fun <D, T : D> Iterable<T>.reduce(
operation: (acc: D, T) -> D //String is literally impossible here, because D is not a String
): S
and written out:
lst<D>.reduce { acc:D, d:D -> }
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
I'm trying to have this compiling:
val criteriaList = aList.stream().map { dateRange -> {
Criteria.where("KEY").`is`(dateRange) } }.toList().toTypedArray()
Criteria().orOperator(*criteriaList)
But:
Criteria().orOperator(*criteriaList)
Currently does not compile:
Type mismatch.
Required:
Array<(out) Criteria!>!
Found:
Array<(() → Criteria!)!>
Why?
You are mapping your dateRange to a () -> Criteria.
You do not need to wrap what is following after -> with curly braces. Check also the Kotlin reference regarding Lambda expression syntax:
val sum = { x: Int, y: Int -> x + y }
A lambda expression is always surrounded by curly braces [...], the body goes after an -> sign. If the inferred return type of the lambda is not
Unit, the last (or possibly single) expression inside the lambda body is treated as the return value.
So you could just write the following instead:
.map { dateRange -> Criteria.where("KEY").`is`(dateRange) }
Note also that you do not really need to call stream(), but you can directly call map on it (except it wouldn't be a real List in the first place).
So your code could probably be simplified to something like:
val criteriaList = aList.map { dateRange -> Criteria.where("KEY").`is`(dateRange) }
.toTypedArray()
or
val criteriaList = aList.map { Criteria.where("KEY").`is`(it) }
.toTypedArray()
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
}
}
Probably a little bit broad question, but the official documentation doesn't even mentioning the arrow operator (or language construct, I don't know which phrase is more accurate) as an independent entity.
The most obvious use is the when conditional statement, where it is used to assign an expression to a specific condition:
val greet = when(args[0]) {
"Appul" -> "howdy!"
"Orang" -> "wazzup?"
"Banan" -> "bonjur!"
else -> "hi!"
}
println(args[0] +" greets you: \""+ greet +"\"")
What are the other uses, and what are they do?
Is there a general meaning of the arrow operator in Kotlin?
The -> is part of Kotlin's syntax (similar to Java's lambda expressions syntax) and can be used in 3 contexts:
when expressions where it separates "matching/condition" part from "result/execution" block
val greet = when(args[0]) {
"Apple", "Orange" -> "fruit"
is Number -> "How many?"
else -> "hi!"
}
lambda expressions where it separates parameters from function body
val lambda = { a:String -> "hi!" }
items.filter { element -> element == "search" }
function types where it separates parameters types from result type e.g. comparator
fun <T> sort(comparator:(T,T) -> Int){
}
Details about Kotlin grammar are in the documentation in particular:
functionType
functionLiteral
whenEntry
The -> is a separator. It is special symbol used to separate code with different purposes. It can be used to:
Separate the parameters and body of a lambda expression
val sum = { x: Int, y: Int -> x + y }
Separate the parameters and return type declaration in a function type
(R, T) -> R
Separate the condition and body of a when expression branch
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
Here it is in the documentation.
From the Kotlin docs:
->
separates the parameters and body of a lambda expression
separates the parameters and return type declaration in a function
type
separates the condition and body of a when expression branch