Avoid else condition in 'When' in kotlin - kotlin

As per documentation of When in Kotlin, else is not mandatory if the compiler knows all the values are covered. This is very in case of emums or sealed class but how to do it in case of arrays for numbers 1 to 5 (startRating).
private fun starMapping(startRating: Int): String {
return when (startRating) {
1 -> "Perfect"
2 -> "Great"
3-> "Okay"
4-> "Bad"
5-> "Terrible"
// don't want to add else as I believe it is prone to errors.
}
}
Something similar to this
return when (AutoCompleteRowType.values()[viewType]) {
AutoCompleteRowType.ITEM -> ItemView(
LayoutInflater.from(parent.context).inflate(R.layout.item_venue_autocomplete_item_info, parent, false))
AutoCompleteRowType.SECTION -> SectionView(
LayoutInflater.from(parent.context).inflate(R.layout.item_venue_autocomplete_section, parent, false)
)
}

Using when statement it is impossible to exclude else clause in case of using ints, because compiler doesn't know what to return if startRating is not in 1..5 range. You can, for example, throw an IllegalStateException if the value is not in the required range:
private fun starMapping(startRating: Int): String {
return when (startRating) {
1 -> "Perfect"
2 -> "Great"
3-> "Okay"
4-> "Bad"
5 -> "Terrible"
else -> throw IllegalStateException("Invalid rating param value")
}
}
Or you can do something like this:
return when {
startRating <= 1 -> "Perfect"
startRating == 2 -> "Great"
startRating == 3 -> "Okay"
startRating == 4 -> "Bad"
else -> "Terrible"
}
But else clause is required.

You may not want to use when for this at all. Here is what I would suggest:
You could create an enum class like so:
enum class Rating(val score: Int) {
Perfect(1),
Great(2),
Okay(3),
Bad(4),
Terrible(5)
}
And utilise it like that:
fun ratingForScore(score: Int) = Rating.values().firstOrNull {
it.score == score
}?.toString()
ratingForScore(1) // "Perfect"

Related

how to change nested if-else to when statements using Kotlin?

I have the following block of code and want to reduce it using Kotlin. How can I do that?
if (name == nameArray[0]) {
// The statement
} else if(name == nameArray[1]) {
// The statement
} else if(name == nameArray[2]) {
// The statement
} else if(name == nameArray[3]) {
// The statement
} else {
// The statement
}
If the array is small and you want to map an action to each index:
You could use indexOfFirst to determine the smallest index which meats your condition. Then you can use a when statement to decide what to do.
when(nameArray.indexOfFirst{ it == name }) {
0 -> // do something
1 -> // do something else
//...
else -> // do something different
}
In case you might want to do the same thing for multiple indices you can use comma separated values. In case the indices are consecutive, you can use ranges:
when(nameArray.indexOfFirst{ it == name }) {
0 -> // do something
1, 2 -> // do the same thing for 1 and 2
in 3..6 -> // do the same thing for 3, 4, 5 and 6
//...
else -> // do something different
}
In order to use this syntax it is a good idea to do index retrieval (like shown) first.
If the array is big and you really only want to check for specific elements:
when(name) {
nameArray[0] -> // do something
nameArray[1] -> // do something
nameArray[2] -> // do something
nameArray[3] -> // do something
else -> // other action
}
You can use when to simplify it as below
when(name) {
nameArray[0] -> //statement
nameArray[1] -> //statement
nameArray[2] -> //statement
nameArray[3] -> //statement
else -> //statement
}
Alternatively, if you can use an enum instead of the nameArray as below
enum class Names {
NAME_1, NAME_2, NAME_3
}
And have name of the Names enum type, you can then use the when clause as below, which is a cleaner way and is more readable
when(name) {
Names.NAME_1 -> //statement
Names.NAME_2 -> //statement
Names.NAME_3 -> //statement
}
You can use a better and more powerful Kotlin construct, when.
It works similarly to switch-case constructs but you can use expression too, check here for more.
Specific to your example you may write:
when (name) {
nameArray[0] -> {
//The statement
}
nameArray[1] -> {
//The statement
}
nameArray[2] -> {
//The statement
}
nameArray[3] -> {
//The statement
}
else -> {
//Executes when conditions are not met
}
}
Maybe I'm misinterpreting what you want to do, but in my opinion a when statement is over-complicating this. In your original code you just want to determine if the array contains the name value in any of the indices from 0 to 3 and respond accordingly.
if ((nameArray.indexOfFirst(name::equals) in 0..3) {
// The statement
} else {
// The else branch
}
Shorter way: Iterate the array instead of if-else every possible index of the array:
fun main(args: Array<String>) {
val nameArray: Array<String> = arrayOf("naam", "nombre", "name", "Name")
val name: String = "name";
for (i in 0..(nameArray.size - 1)) {
if (name == nameArray[i]) {
println("The statement should be executed on index " + i)
}
}
}
Output:
The statement should be executed on index 2

Kotlin idomatic way to get columns of connect four game when hovering over it

I want to realize a game of four . The new chip will hover under the mouse cursor and accoring to its x-coordinates I want to calculate the column over which it hovers atm (and where it will be inserted after a click)
At the moment I do sth like this:
fun whichColumnIsChip(chip : Chip) : Int{
val x = chip.x/2
val columnWidth = Chip.radius*2 + distanceBetweenColumns
val rightColumnBorder = IntArray(gamefield.columns.size){ i -> marginLeft+(i+1) * (Chip.radius*2 + distanceBetweenColumns) }
when {
x.betweenInclusive(0.0, rightColumnBorder[0].toDouble()) -> return 0
x.betweenInclusive(rightColumnBorder[0].toDouble(), rightColumnBorder[1].toDouble()) -> return 1
x.betweenInclusive(rightColumnBorder[1].toDouble(), rightColumnBorder[2].toDouble()) -> return 2
x.betweenInclusive(rightColumnBorder[2].toDouble(), rightColumnBorder[3].toDouble()) -> return 3
x.betweenInclusive(rightColumnBorder[3].toDouble(), rightColumnBorder[4].toDouble()) -> return 4
x.betweenInclusive(rightColumnBorder[4].toDouble(), rightColumnBorder[5].toDouble()) -> return 5
x.betweenInclusive(rightColumnBorder[5].toDouble(), rightColumnBorder[6].toDouble()) -> return 6
else -> return -10
}
}
So if my cursor hovers over in between the x-coordinates of the 3rd column I just want to return 3
The code above works as intended, but I am sure there is a much more idomatic and shorter way
Few improvements could be done
1) Since Kotlin's when is not only a statement but an expression as well, you can reduce this
when {
condition1 -> return 1
...
conditionN -> return N
}
to
return when {
condition1 -> 1
...
conditionN -> N
}
2) You can reduce copypaste by declaring a function that performs a check on given array and some index. In following example the function is declared as local in order to capture local variables x and rightColumnBorder, but it could be declared as regular or extension, it's up to you.
fun option1(): Int {
//...
val test = fun (i: Int) = x.betweenInclusive(
if (i > 0) rightColumnBorder[i - 1].toDouble() else 0.0,
rightColumnBorder[i].toDouble())
return when {
test(0) -> 0
test(1) -> 1
test(2) -> 2
test(3) -> 3
test(4) -> 4
test(5) -> 5
test(6) -> 6
else -> -10
}
}
However, in your particular case you can notice that returned result is determined by the given array index, so the whole when statement could be replaced with a loop:
fun option2(): Int {
//...
rightColumnBorder.forEachIndexed { idx, value ->
val from = if (idx > 0) rightColumnBorder[idx - 1].toDouble() else 0.0
if (x.betweenInclusive(from, value.toDouble())) {
return idx
}
}
return -10
}
OR
fun option3(): Int {
//...
val test = fun (i: Int) = x.betweenInclusive(
if (i > 0) rightColumnBorder[i - 1].toDouble() else 0.0,
rightColumnBorder[i].toDouble())
rightColumnBorder.forEachIndexed { i, _ -> if (test(i)) return i }
return -10
}

Find-first-and-transform for Sequence in Kotlin

I often stumble upon this problem but don't see a common implementation: how do I idiomatically (functionally) find an element, stop search after the match, and also return a different type (i.e. map whatever matched to another type)?
I've been able to do a workaround with
fun <F,T> Sequence<F>.mapFirst(block: (F) -> T?): T? =
fold(AtomicReference<T>()) { ref, from ->
if (ref.get() != null) return#fold ref
ref.set(block(from))
ref
}.get()
fun main() {
Files.list(someDir).asSequence().map { it.toFile() }.mapFirst { file ->
file.useLines { lines ->
lines.mapFirst { line ->
if (line == "123") line.toInt() else null
}
}
}?.let { num ->
println("num is $num") // will print 123 as an Int
} ?: println("not a single file had a line eq to '123'")
}
But that doesn't stop on the match (when block() returns non-null) and goes to consume all files and all their lines.
A simple for loop is enough to implement mapFirst:
fun <F,T> Sequence<F>.mapFirst(block: (F) -> T?): T? {
for (e in this) {
block(e)?.let { return it }
}
return null
}
If you need a solution without introducing your own extensions (though there's nothing wrong with it), you can use mapNotNull + firstOrNull combination:
files.asSequence()
.mapNotNull { /* read the first line and return not null if it's ok */ }
.firstOrNull()
I would not map the values you discard then, instead do it like this:
sequenceOf(1, 2, 3)
.firstOrNull() { it == 2 }
?.let { it * 2 } ?: 6
First you find the value that matches your condition, then you transform it too whatever you want. In case you don't find a matching element, you assign a default value (in this case 6).

How to implement switch-case statement in Kotlin

How to implement equivalent of following Java switch statement code in Kotlin?
switch (5) {
case 1:
// Do code
break;
case 2:
// Do code
break;
case 3:
// Do code
break;
}
You could do like this:
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
extracted from official help
switch in Java is effectively when in Kotlin. The syntax, however, is different.
when(field){
condition -> println("Single call");
conditionalCall(field) -> {
print("Blocks");
println(" take multiple lines");
}
else -> {
println("default");
}
}
Here's an example of different uses:
// This is used in the example; this could obviously be any enum.
enum class SomeEnum{
A, B, C
}
fun something(x: String, y: Int, z: SomeEnum) : Int{
when(x){
"something" -> {
println("You get the idea")
}
else -> {
println("`else` in Kotlin`when` blocks are `default` in Java `switch` blocks")
}
}
when(y){
1 -> println("This works with pretty much anything too")
2 -> println("When blocks don't technically need the variable either.")
}
when {
x.equals("something", true) -> println("These can also be used as shorter if-statements")
x.equals("else", true) -> println("These call `equals` by default")
}
println("And, like with other blocks, you can add `return` in front to make it return values when conditions are met. ")
return when(z){
SomeEnum.A -> 0
SomeEnum.B -> 1
SomeEnum.C -> 2
}
}
Most of these compile to switch, except when { ... }, which compiles to a series of if-statements.
But for most uses, if you use when(field), it compiles to a switch(field).
However, I do want to point out that switch(5) with a bunch of branches is just a waste of time. 5 is always 5. If you use switch, or if-statements, or any other logical operator for that matter, you should use a variable. I'm not sure if the code is just a random example or if that's actual code. I'm pointing this out in case it's the latter.
The switch case is very flexible in kotlin
when(x){
2 -> println("This is 2")
3,4,5,6,7,8 -> println("When x is any number from 3,4,5,6,7,8")
in 9..15 -> println("When x is something from 9 to 15")
//if you want to perform some action
in 20..25 -> {
val action = "Perform some action"
println(action)
}
else -> println("When x does not belong to any of the above case")
}
When Expression
when replaces the switch operator of C-like languages. In the simplest form it looks like this
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
when matches its argument against all branches sequentially until some branch condition is satisfied. when can be used either as an expression or as a statement. If it is used as an expression, the value of the satisfied branch becomes the value of the overall expression. If it is used as a statement, the values of individual branches are ignored. (Just like with if, each branch can be a block, and its value is the value of the last expression in the block.)
From https://kotlinlang.org/docs/reference/control-flow.html#when-expression
when defines a conditional expression with multiple branches. It is similar to the switch statement in C-like languages. Its simple form looks like this.
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
when matches its argument against all branches sequentially until some branch condition is satisfied.
when can be used either as an expression or as a statement. If it is used as an expression, the value of the first matching branch becomes the value of the overall expression. If it is used as a statement, the values of individual branches are ignored. Just like with if, each branch can be a block, and its value is the value of the last expression in the block.
import java.util.*
fun main(args: Array<String>){
println("Hello World");
println("Calculator App");
val scan=Scanner(System.`in`);
println("""
please choose Your Selection to perform
press 1 for addition
press 2 for substraction
press 3 for multipication
press 4 for divider
press 5 for divisible
""");
val opt:Int=scan.nextInt();
println("Enter first Value");
val v1=scan.nextInt();
println("Enter Second Value");
val v2=scan.nextInt();
when(opt){
1->{
println(sum(v1,v2));
}
2->{
println(sub(v1,v2));
}
3->{
println(mul(v1,v2));
}
4->{
println(quotient(v1,v2));
}
5->{
println(reminder(v1,v2));
}
else->{
println("Wrong Input");
}
}
}
fun sum(n1:Int,n2:Int):Int{
return n1+n2;
}
fun sub(n1:Int, n2:Int):Int{
return n1-n2;
}
fun mul(n1:Int ,n2:Int):Int{
return n1*n2;
}
fun quotient(n1:Int, n2:Int):Int{
return n1/n2;
}
fun reminder(n1:Int, n2:Int):Int{
return n1%n2;
}
Just use the when keyword. If you want to make a loop, you can do like this:
var option = ""
var num = ""
while(option != "3") {
println("Choose one of the options below:\n" +
"1 - Hello World\n" +
"2 - Your number\n" +
"3 - Exit")
option = readLine().toString()
// equivalent to switch case in Java //
when (option) {
"1" -> {
println("Hello World!\n")
}
"2" -> {
println("Enter a number: ")
num = readLine().toString()
println("Your number is: " + num + "\n")
}
"3" -> {
println("\nClosing program...")
}
else -> {
println("\nInvalid option!\n")
}
}
}
val operator = '+'
val a = 6
val b = 8
val res = when (operator) {
'+' -> a + b
'-' -> a - b
'*' -> a * b
'/' -> a / b
else -> 0
}
println(res);
We use the following code for common conditions
val operator = '+'
val a = 6
val b = 8
val res = when (operator) {
'+',
'-' -> a - b
'*',
'/' -> a / b
else -> 0
}
println(res);
In kotlin, their is no switch-case statement. But we have when expression similar to switch. Just like if-else or switch, first all conditions are checked, if none matches then else code evaluated.
when (n) {
1 -> {
print("First")
// run your code
}
2 -> print("Second")
3, 4 -> print("Third or Forth") // check multiple conditions for same code
in 1..100 -> print("Number is in the range")
else -> {
print("Undefined")
}
}
There is no need of any break as of switch case.
Here is an example to know Using “when” with arbitrary objects,
VehicleParts is a enum class with four types.
mix is a method which accepts two types of VehicleParts class.
setOf(p1, p2) - Expression can yield any object
setOf is a kotlin standard library function that creates Set containing the objects.
A set is a collection for which the order of items does not matter.
Kotlin is allowed to combine different types to get mutiple values.
When I pass VehicleParts.TWO and VehicleParts.WHEEL, I get "Bicycle".
When I pass VehicleParts.FOUR and VehicleParts.WHEEL, I get "Car".
Sample Code,
enum class VehicleParts {
TWO, WHEEL, FOUR, MULTI
}
fun mix(p1: VehicleParts, p2: VehicleParts) =
when (setOf(p1, p2)) {
setOf(VehicleParts.TWO, VehicleParts.WHEEL) -> "Bicycle"
setOf(VehicleParts.FOUR, VehicleParts.WHEEL) -> "Car"
setOf(VehicleParts.MULTI, VehicleParts.WHEEL) -> "Train"
else -> throw Exception("Dirty Parts")
}
println(mix(VehicleParts.TWO,VehicleParts.WHEEL))
If You want to print or open multiple Activities using switch case (When) in Kotlin then use this code.. Thank you..
var dataMap: Map<String?, String?> = HashMap()
var noteType: String? = ""
when (noteType) {
"BIGTEXT" -> NEW_PAGE(dataMap)
"NORMAL" -> NORMAL_PAGE(dataMap)
"ABOUT"->ABOUT_PAGE((dataMap))
}

Kotlin: Return can be lifted out of 'when'

The alternative to switch in Kotlin is when. So, inside a recycler view adapter, when I am returning view type, I use when:
override fun getItemViewType(position: Int): Int {
when (position) {
0 -> return ItemViewType.TITLE.type
1 -> return ItemViewType.SUBTITLE.type
2 -> return ItemViewType.ITEM.type
else -> return -1
}
}
But, the above statement sends our warning message Return can be lifted out of 'when'.
Does anyone know what may be the correct way of using when? And what should be done to fix the above case?
You’re using when like a simple Java switch statement, which is okay but not very idiomatic and can be improved. You can refactor your code in two steps:
Kotlin's when can be used as an expression, it returns a value if you wish:
override fun getItemViewType(position: Int): Int {
return when (position) {
0 -> ItemViewType.TITLE.type
1 -> ItemViewType.SUBTITLE.type
2 -> ItemViewType.ITEM.type
else -> -1
}
}
The function body, now consisting of a single statement, can be changed into an expression body:
override fun getItemViewType(position: Int) = when (position) {
0 -> ItemViewType.TITLE.type
1 -> ItemViewType.SUBTITLE.type
2 -> ItemViewType.ITEM.type
else -> -1
}
Your when is correct, however Kotlin has the ability to lift the return out of the 'when' if you are returning in every case, thus it becomes :
override fun getItemViewType(position: Int): Int {
return when (position) {
0 -> ItemViewType.TITLE.type
1 -> ItemViewType.SUBTITLE.type
2 -> ItemViewType.ITEM.type
else -> -1
}
}
In Kotlin, several statements, including if, when and try, can return a value. So in your case, you can refactor the statement to have the when statement return the actual value, which you can then return from the function.
So, you can simplify your method to the following:
override fun getItemViewType(position: Int): Int = when (position) {
0 -> ItemViewType.TITLE.type
1 -> ItemViewType.SUBTITLE.type
2 -> ItemViewType.ITEM.type
else -> -1
}