Kotlin when change value - kotlin

can i use when in Kotlin to change the value of an existing variable?
i understand this one:
val num = 1
val result = when {
num > 0 -> "positive"
num < 0 -> "negative"
else -> "zero"
}
println(result)
should be "positive"
but i want this( psuedo )....
val num = 1
var result = "init string"
//bunch of code here
// later....
result = when{
num > 0 -> "positive"
num < 0 -> "negative"
else -> "zero"
}
it writes positive, but it says "Variable "result" initializer is redundant.
What im missing here?
Thx

The line
var result = "init string"
is redundant because you leave no possibility of that value not getting overridden in the when statement. "init string" cannot be printed at the end of the code, the variable will always have one of the three possible values defined in the when.
But you can directly initialize result with the when statement:
fun main() {
var num = 1
// directly assign the result of the when statment to the variable
var result = when {
num > 0 -> "positive"
num < 0 -> "negative"
else -> "zero"
}
println(result)
}
This prints
positive

thank you for the answer.
But i can say....
fun main() {
var num = 1
// directly assign the result of the when statment to the variable
var result = when {
num > 0 -> "positive"
num < 0 -> "negative"
else -> "zero"
}
println(result)
// and then later reassign....
num = 2
result = when{
num == 2 -> "two"
else -> "not two"
{
println(result)
// prints two
}
So i cant reassign an existing string with a when statement, that is not initialized with an another when statement?
I hope this is readable. :)
Thank you.
dagogi

Related

How to replace sumBy with sumOf in kotlin 1.5

I have the following code:
val sum = list.itens.sumBy { item ->
when (item.intValueI == item.intValueII) {
true -> 1
else -> 0
}
}
Updating to Kotlin 1.5, I got the deprecated warning; How should I proceed to achieve the same functionality?
I tried below:
val result = list.itens.sumOf<ListItemClass> { item ->
val intValueI = item.value ?: 0
val intValueII = item.valueII ?: 0
when(item.intValueI == item.IntValueII){
true -> 1
else -> 0
}
}
Why not filter your items and count them instead of involving maths, since that seems like what you're after?
val sum = list.items.filter { it.intValue1 == it.intValue2 }.size
Or, even more simply (thanks Tenfour04 in comments!):
val sum = list.items.count { it.intValue1 == it.intValue2 }
If it's absolutely essential for some reason to use sumOf, a slightly hacky solution would be:
val sum = list.items.sumOf {
if (it.intValue2 == it.intValue2) 1 else 0 as Int
}
Looks like the compiler / Android Studio gets a bit confused when returning 1 & 0, hence needing an else of either it.intValue1 * 0 or 0 as Int. The former is messier, but the latter leaves a AS warning.

I can't figure out how to get the largest element in loop

I don't understand the loop in the following content.
There is a sequence of integer numbers (which ends with the number 0). Find the largest element of this sequence.
The number 0 itself is not included in the sequence. It serves only as a sign that there are no more numbers.
I tried various methods, but they did not solve the problem.
The way to exit the loop at 0 is to use
While(i ! = 0){}
Here is the code we created.
fun main(args: Array<String>) {
val scanner = Scanner(System.`in`)
var count = scanner.nextInt()
do {
var input = scanner.nextInt()
while(input != 0 || count != 0) {
if (count < input){
count = input
}
}
}while(input != 0 || count != 0)
println(count)
}
You can use maxOf() for example:
fun readLn() = readLine()!!
fun readIntOrStopLoopWithZero() = readLn().toIntOrNull() ?: 0
fun readMaxInteger() {
var maxInput: Int = 0
//getting first user input
var userInput = readIntOrStopLoopWithZero()
while (userInput!= 0) {
//calc max
maxInput = maxOf(userInput, maxInput)
//getting user input again.
userInput= readIntOrStopLoopWithZero()
}
if (maxInput == 0)
//In case we get 0 as first input.
print("max value not found")
else
print("Max input is : $maxInput")
}
readMaxInteger()
Input:
1
2
0
output:
Max input is 2
Input:
1
0
output:
Max input is 1
Input:
0
output:
max value not found
Input:
1
2
3
Word
output:
Max input is : 3
Problems:
You've over-complicated it by creating a nested loop.
You're checking input != 0 to exit either of the loops, so it will exit immediately on the first element.
So fixing these problems, you have:
fun main(args: Array<String>) {
val scanner = Scanner(System.`in`)
var count = 0 // start with 0 for simplicity
do {
val input = scanner.nextInt()
if (count < input) {
count = input
}
} while (input != 0)
println(count)
}
And here's an alternate solution. Make a Sequence so it becomes an Iterable and you can use maxOrNull() on it:
fun main(args: Array<String>) {
val sequence = sequence {
val scanner = Scanner(System.`in`)
do {
val input = scanner.nextInt()
yield(input)
} while (input != 0)
}
println(sequence.maxOrNull() ?: 0)
}
I wanted to finish within a single function, so I chose the following.
fun main(args: Array<String>) {
val scanner = Scanner(System.`in`)
var count = 0 // start with 0 for simplicity
do {
val input = scanner.nextInt()
if (count < input) {
count = input
}
} while (input != 0)
println(count)
}
Thanks to everyone who responded!

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
}

How and when does kotlin let run?

for all the examples on the internet i cant figure out when and how is kotlins let ran?
if(phones.size == 0){
phones.add("")
}
return phones[0]
so if phones list size is 0, we add empty string and return that instead.
Now how would one do same with let ?
phones.let {
return ""
}
does this work with size 0, or do i have to have null list?
do i need return keyword, if yes, where?
is the above fun always going to return empty string? or just when phones is null?
when is this let code block even ran?
Update:
val cakes = listOf("carrot", "cheese", "chocolate")
fun main(args: Array<String>) {
var cakesEaten = 0
while (cakesEaten < 3) { // 1
cakesEaten ++
val result = cakes?.let{
if(cakesEaten == 2) {
"HeyLo"
} else {
2
}
}
println("result value = $result")
when(result) {
is String -> println(" result variable is a String")
is Int -> println(" result variable is Integer")
}
}
}
result value = 2
result variable is Integer
result value = HeyLo
result variable is a String
result value = 2
result variable is Integer
Original post
If your 'phones' Object is a Nullable type,
val result = phones?.let{
// this block runs only if phones object is not null
// items can be accessed like it.size
// expression result will be returned. no need to mention return.
if(it.size == 0) {
it.add("")
it[0]
} else it.size
}
result value will be either it[0] or it.size and its type will be Any.
But if this the functionality you need you can check Markos solution.
If you're interested in how to write your logic in Kotlin's FP idiom, it doesn't involve let at all:
phones.takeIf { it.isEmpty() }?.add("")
return phones[0]
However, I don't find this idiom better than what you started out with.

Kotlin: Variable 'result' must be initialized

The compiler is showing error Kotlin: Variable result must be initialized.
Here is the code.
fun main(args: Array<String>) {
print("Enter two numbers: ")
// nextDouble() reads the next double from the keyboard
var first= readLine()!!.toDouble()
var second = readLine()!!.toInt()
print("Enter an choice(1-4)): ")
val operator = readLine()!!.toInt()
var result: Double
when (operator) {
1 -> result = first + second
2 -> result = first - second
3 -> result = first * second
4 -> result = first / second
else -> {
println("Error.")
}
}
println("The result is :- " +result)
}
The problem is that when you read the value of result here:
println("The result is :- " +result)
result might not be initialized, because here:
var result: Double
when (operator) {
1 -> result = first + second
2 -> result = first - second
3 -> result = first * second
4 -> result = first / second
else -> {
println("Error.")
}
You are NOT assigning a value to result in the else branch. You have several options, one could be to make result nullable. For instance:
var result = when (operator) {
1 -> first + second
2 -> first - second
3 -> first * second
4 -> first / second
else -> null
}
if (result != null) {
println("The result is :- " + result)
} else {
println("Error.")
}
Notice that if the operator is not (1-4) the value of result will be null. Also, in your code you're printing "Error" and then again you're attempting to print the result.
To add something more, you could make the thing a bit nicer if you defined your operator with the when statement as a method reference:
print("Enter an choice(1-4)): ")
val operatorCode = readLine()!!.toInt()
val operator: ((Int) -> Double)? = when (operatorCode) {
1 -> first::plus
2 -> first::minus
3 -> first::times
4 -> first::div
else -> null
}
if (operator != null) {
val result = operator.invoke(second)
println("The result is :- " + result)
} else {
println("Error.")
}
As jrtapsell points out the main issue is that you declare result to be a Double but you since you don't cover every possible value in the when statement, you are accessing an un-initialized variable.
Another option, if you really operators other than 1-4 want it to be an error condition and don't want result to end up with a valid Double is to use Optionals.
fun main(args: Array<String>) {
print("Enter two numbers: ")
// nextDouble() reads the next double from the keyboard
var first= readLine()!!.toDouble()
var second = readLine()!!.toInt()
print("Enter an choice(1-4)): ")
val operator = readLine()!!.toInt()
var result: Double? = null
when (operator) {
1 -> result = first + second
2 -> result = first - second
3 -> result = first * second
4 -> result = first / second
else -> {
println("Error.")
}
}
// only print the result if not null
result?.let {
println("The result is :- " +result)
}
}
In this case result will be null if the operator is not one of 1-4.
What is the error message trying to tell you
Variable 'result' must be initialized.
This means there are paths through your code where you access result before you have given it a value.
This is a problem because it means you have probably missed off handling a condition.
How do you get through your code without setting result
If the user inputs 5 result is not set
How to fix
Either set result in the else branch, or give it a default value in the declaration