Call a member function in a case of a when statement? - kotlin

For example, Char has a member function isLetter(). Is there any way to call it in a case? The code below does not work.
var ch:Char = null;
when(ch)
{
'*' -> print("You typed an asterisk.");
isLetter() -> print("You typed a letter.");
else -> print("You typed something.");
}

The uses of the parameter to when are quite restrictive: equality, order with < &c, inclusion with in, type checking with is, and that's about it.  So for anything more complex, it's usually easier not to specify a parameter.  In this case, the following isn't much less concise or clear:
when {
ch == '*' -> print("You typed an asterisk.")
ch.isLetter() -> print("You typed a letter.")
else -> print("You typed something.")
}
Some points worth noting:
Semicolons are not needed in Kotlin (except for a few rare ambiguous cases such as putting multiple statements on a line — which is rarely a good idea).
print() does not add a trailing newline, which can cause problems; println() is more common.
var ch: Char = null won't compile, because the type Char is not nullable.  Either give a non-null default value, or make it nullable by specifying the type as Char?.  However, in the latter case, you won't be able to call ch.isLetter(), because that would risk a NullPointerException.  So you'd either need to add a null case before that, or handle the null in that check, either with ch != null && ch.isLetter(), or ch?.isLetter() == true — both of which are ugly.

There is, but there's also the possibility of it not being applicable in your situation.
when can be used without an argument, like this:
fun main() {
val ch: Char = 'd'
when {
ch.isLetter() -> println("It's a letter")
ch.isDigit() -> println("It's a number")
else -> println("It's neither a letter, nor a number")
}
}
But if you make ch nullable like var ch: Char? = null, you won't be able to call member functions in that when clause.

You can check whether ch is in a specific CharCategory. For isLetter() those are the following five:
when(ch)
{
'*' -> print("You typed an asterisk.")
in CharCategory.UPPERCASE_LETTER,
in CharCategory.LOWERCASE_LETTER,
in CharCategory.TITLECASE_LETTER,
in CharCategory.MODIFIER_LETTER,
in CharCategory.OTHER_LETTER -> print("You typed a letter.")
else -> print("You typed something.")
}

first in your code var ch:Char = null
it must be Char?
the solution is simple don't put argument:
var ch:Char = 'c'
when {
ch == '*' -> println("*")
ch.isLetter() -> println("letter")
ch.isDigit() -> println("digit")
}
hope my answer helped you

Related

How do I replace letters with numbers using the replace function in kotlin inside a lambda expression

mood = "leet"
modifier = { message ->
val regex = """(L|e|t)""".toRegex()
//Clueless about what to do after this
}
THIS IS WHAT I CAME UP WITH SO FAR, THE QUESTION IN THE BOOK BIG NERD RANCH KOTLIN EDITION 2 SAYS "leet (or 1337): The narrator will speak in leetspeak, replacing letters with numbers and symbols that look similar. For example, ‘L’ becomes ‘1’; ‘E’ becomes ‘3’; ‘T’ becomes ‘7’. (Hint: Take a look at String’s replace function. There is a version that accepts a lambda as the second parameter.)"
This is the function they're telling you to look at, specifically this one:
inline fun CharSequence.replace(
regex: Regex,
noinline transform: (MatchResult) -> CharSequence
): String
Returns a new string obtained by replacing each substring of this char sequence that matches the given regular expression with the result of the given function transform that takes MatchResult and returns a string to be used as a replacement for that match.
So the lambda you provide is a function that takes a MatchResult
and does something with it, and returns a CharSequence (which can be a one-character long String). The replace function calls that lambda for every match that regex makes.
You get the general idea of what you're supposed to do? You have two parts here - the thing that identifies parts of the input string to process, and the thing that takes those matches and changes them into something else. The result is the original string with those changes made. So you need to come up with a regex and a transform that work together.
Nobody (probably) is going to tell you the answer because the point is figuring it out for yourself, but if you have any questions about things like regexes people will be happy to help you out! And speaking of, this site is extremely useful (I just used it myself to check I knew what I was doing): https://regex101.com/
Here is the implementation as pointed by #cactustictacs :
5 -> {
mood = "leet"
val regex: Regex = """[LET]""".toRegex()
modifier = { message ->
message.uppercase().replace(regex) { m ->
when (m.value) {
"L" -> "1"
"E" -> "3"
"T" -> "7"
else -> ""
}
}
}
}
and here is the another method almost same but with minor change using regex.replace()
5 -> {
mood = "leet"
val regex: Regex = """[LET]""".toRegex()
modifier = { message ->
regex.replace(message.uppercase()){m ->
when (m.value) {
"L" -> "1"
"E" -> "3"
"T" -> "7"
else -> ""
}
}
}
}
You can use it in place of m to make it slightly more concise.

Kotlin toSortedMap() with custom Comparator not working properly

I need to group and sort an API result by 1st character of name. All names starting with a letter should come in order, followed by names starting with non-alphabet grouped into "#" and placed at end. Below is a simplified code:
data class Model(val title: String)
fun main() {
val mList = arrayListOf(Model("pqr"), Model("abc"), Model("3AM"), Model("%s5"))
val mComparator = Comparator<String> { o1, o2 ->
when {
o1 == "#" -> 1
o2 == "#" -> -1
else -> o1.compareTo(o2)
}
}
val mMap = mList.groupBy {
if (it.title[0].isLetter()) it.title[0].toString()
else "#"
}.toSortedMap(mComparator)
val outList = arrayListOf<List<Model>?>()
mMap.keys.forEach {
outList.add(mMap[it])
}
println(mMap)
println(mMap.keys)
println(mMap.values)
println(mMap["#"])
println(outList)
}
Here's output of above code:
{a=[Model(title=abc)], p=[Model(title=pqr)], #=[Model(title=3AM), Model(title=%s5)]}
[a, p, #]
[[Model(title=abc)], [Model(title=pqr)], [Model(title=3AM), Model(title=%s5)]]
null
[[Model(title=abc)], [Model(title=pqr)], null]
The problem is that mMap["#"] gives null although mMap, mMap.keys, mMap.values show "#" in keys and also the corresponding values.
Don't understand why mMap["#"] returns null rather than [Model(title=3AM), Model(title=%s5)]
Your comparator breaks the contract that a Comparator must satisfy. It must be reflexive, meaning it gives the exact opposite result if you swap the order of the two inputs. A corrected version of your comparator:
val mComparator = Comparator<String> { o1, o2 ->
when {
o1 == "#" && o2 != "#" -> 1
o2 == "#" && o1 != "#" -> -1
else -> o1.compareTo(o2)
}
}
TreeMap depends on a correct Comparator for its functionality. Apparently, its comparator is even involved when using treeMap.get such that in your case it returns the nonsensical result of null.
By the way, putting an "m" prefix before a variable name is Hungarian notation that means the prefix is a member variable. But these are not member variables. They are local variables. Kotlin doesn't even have member variables, but properties are analogous. So, you should not be using "m" prefixes here because it communicates incorrectly about the meaning of your code. Anyway, most developers avoid using Hungarian notation because it makes code harder to read. A lot of official Android examples use it because of the Android development team's internal code style, but that is not a recommendation that you use it in your own code.
val mComparator = Comparator<String> { o1, o2 ->
when {
(o1 == "#").xor(o2 == "#") -> if (o1 == "#") 1 else -1
else -> o1.compareTo(o2)
}
}

Distinguish functions with lambda argument by lambda's return type?

I have a function timeout(...) (extension function that returns this) which accepts an argument that is either String, Date or Long. What I am trying to do is to make it accept any lambda that also returns one of these three types.
Kotlin finds the below functions ambiguous and can't decide which one to call when I type, for example, timeout { "something" }.
#JvmName("timeoutString")
fun <CR: CachableResponse> CR.timeout(timeLambda: CR.()->String): CR = timeout(timeLambda())
#JvmName("timeoutLong")
fun <CR: CachableResponse> CR.timeout(timeLambda: CR.()->Long): CR = timeout(timeLambda())
#JvmName("timeoutDate")
fun <CR: CachableResponse> CR.timeout(timeLambda: CR.()->Date): CR = timeout(timeLambda())
The error I'm getting is Cannot choose among the following candidates without completing type inference.
Of course one way to work around this, is to have one function instead of three like this:
fun <CR: CachableResponse, Type> CR.timeout(timeLambda: CR.()->Type): CR =
timeLambda().let { when (it) {
is String -> timeout(it)
is Date -> timeout(it)
is Long -> timeout(it)
else -> this
} }
In this case, though, the developer won't have any clue what its lambda will have to return without reading the description or checking the source code.
Is there any more elegant solution?
Actually, you solution is rather elegant.
I would only suggest to inline CR generic parameter and capture when subject in a variable:
fun <Type> CachableResponse.timeout(timeLambda: CachableResponse.() -> Type) =
when (val it = timeLambda()) {
is String -> timeout(it)
is Date -> timeout(it)
is Long -> timeout(it)
else -> this
}
In this case, though, the developer won't have any clue what its lambda will have to return without reading the description or checking the source code.
IDE comes to the rescue:

Kotlin: Operator '!=' cannot be applied to 'String?' and 'Char.Companion'

In this Hangman game how can I give the condition to check if input !=Char? It says that Kotlin: Operator '!=' cannot be applied to 'String?' and 'Char.Companion'
How can I solve this issue?
while (letters != correctGuesses) {
printExploredWord(word, correctGuesses)
println("\n#Wrong guesses: $fails\n\n")
print("Guess letter:")
val input = readLine()
if (input == null) {
continue
} else if (input.length != 1) {
println("Please enter one letter")
continue
} else if (input != Char) {
println("Please enter a character")
}
if (word.toLowerCase().contains(input.toLowerCase())) {
correctGuesses.add(input[0].toLowerCase())
} else {
++fails
}
Sounds from your comments that what you want to check is if the input String? has a single alphabetic character. You need to be precise with your terminology. Numbers and punctuation are also made up of characters. Char is a class representing any element of a String, so it doesn't make sense to be asking if something in a String is a Char because the answer is true no matter what.
The question you need to be asking is whether the first character in the given String is a letter. There's a function for that: Char.isLetter(). And since we're checking the content of the first character of the String, we need to get its value with input[0] because it doesn't make sense to ask if a whole String is a letter character. A String is never a Char because these are different classes. So in your case you would use:
if (input == null) {
continue
} else if (input.length != 1) {
println("Please enter one letter")
continue
} else if (!input[0].isLetter()) {
println("Please enter a character")
continue
}
But again, the terminology is wrong here. You should be reminding the user to enter a letter, not a character.
Your input variable is a type String?, so there's no reason to check if it's a char or not. To check for instanceof in Kotlin you use the "is" operator. So to compare if your input is a Char you can do,
else if(!(input is Char)) {
//This is unnecessary though since you're already checking if the length of the input isn't 1
}

What does the arrow ("->") operator do in Kotlin?

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