Setter not assigning value in Kotlin - kotlin

I am trying to do a temperature program, which outputs the lowest temperature from three cities provided. If the temperature of one of three cities is above + 57 or below -92 all three cities will have set default values which are (+5 Moscow, +20 Hanoi , 30 for Dubai)
However providing those numbers 20,100,35 in readLine doesn't work.
This is how City class looks like:
class City(val name: String) {
var degrees: Int = 0
set(value) {
field =
if (value > 57 || -92 > value) {
when (this.name) {
"Dubai" -> 30
"Moscow" -> 5
"Hanoi" -> 20
else -> 0
}
} else {
value
}
}}
And in my main I have:
val first = readLine()!!.toInt()
val second = readLine()!!.toInt()
val third = readLine()!!.toInt()
val firstCity = City("Dubai")
val secondCity = City("Moscow")
val thirdCity = City("Hanoi")
firstCity.degrees = first
secondCity.degrees = second
thirdCity.degrees = third
println(first)
println(second)
println(third)
What's wrong in the setter? Why does the second doesn't set the default values?

Works as expected to me https://pl.kotl.in/otINdg8E3:
class City(val name: String) {
var degrees: Int = 0
set(value) {
field =
if (value > 57 || -92 > value) {
when (this.name) {
"Dubai" -> 30
"Moscow" -> 5
"Hanoi" -> 20
else -> 0
}
} else {
value
}
}}
fun main() {
val firstCity = City("Dubai")
val secondCity = City("Moscow")
val thirdCity = City("Hanoi")
firstCity.degrees = 100
secondCity.degrees = -100
thirdCity.degrees = 6
println(firstCity.degrees) // prints 30
println(secondCity.degrees) // prints 5
println(thirdCity.degrees) // prints 6
}

Related

Different Property setter for three same class objects in Kotlin

What I am trying to implement are three different temperature values depending on the city name.
The following class:
class City(val name: String) {
var degrees: Int = 0
set(value) {
when(this.name){
("Dubai") -> 30
"Moscow" -> 5
"Hanoi" -> 20
}
field = value
}}
And main func:
fun main() {
val firstCity = City("Dubai")
val secondCity = City("Moscow")
val thirdCity = City("Hanoi")
println(firstCity.degrees) // 0
}
Why is it set to default value 0? For Dubai it should have been 30.
The degrees are initialized with 0 and never changed due to no invocation of the setter, which lacks a value for cities that are not expected (maybe that's why you initialized the degrees?).
You could do what you want way shorter:
class City(val name: String) {
var degrees: Int = when(name) {
"Dubai" -> 30
"Moscow" -> 5
"Hanoi" -> 20
else -> 0 // default value for unpredictable cities
}
}
fun main() {
val firstCity = City("Dubai")
val secondCity = City("Moscow")
val thirdCity = City("Hanoi")
println(firstCity.degrees)
}
This will output 30

Kotlin Integer.MAX_VALUE Returns Negative Number

Expected
Use Integer.MAX_VALUE in order to consistently return a large number for the purposes of comparison.
Observed
Integer.MAX_VALUE is returning a negative number.
Implement
In the sample code values are saved into a 2D table in order to find the minimum amount of coins required to make up a given amount.
Using Integer.MAX_VALUE
-2147483647 is being derived from Integer.MAX_VALUE.
fun main() {
// Steps - Iterative/bottom-up
// 1. Create a 2D table: Rows = Denominations(Denoms), Columns = Amount(Amt)
// 2. Store min # of coins in at [R][C] = Min(currentDenomMin, previousDenomMin)
// a. currentDenomMin = [R][C - coins.get(R)] + 1
// b. previousDenomMin = [R - 1][C]
// 3. Return minCount or -1 for table[coins.size - 1, Amt].
println("Min count: ${coinChange(intArrayOf(2), 3)}")
}
lateinit var table: Array<IntArray>
lateinit var mCoins: IntArray
private val maxValue = Integer.MAX_VALUE
fun coinChange(coins: IntArray, amt: Int): Int {
table = Array(coins.size, { IntArray(amt + 1) })
mCoins = coins
coins.sort()
buildMinCounts(amt)
val minCount = table[coins.size - 1][amt]
return if (minCount == maxValue) -1 else minCount
}
fun buildMinCounts(amt: Int) {
for (r in 0..mCoins.size - 1) {
for (c in 0..amt) {
val currentDenomValue = mCoins.get(r)
val currentDenomMin = getDenomMin(r, c - currentDenomValue) + 1
val previousDenomMin = getDenomMin(r - 1, c)
if (c == 0) {
table[r][c] = 0
} else table[r][c] = Math.min(currentDenomMin, previousDenomMin)
}
}
}
fun getDenomMin(r: Int, c: Int): Int {
if (r < 0 || c < 0) return maxValue
else return table[r][c]
}
fun printT(amt: Int) {
for (r in 0..mCoins.size - 1) {
for (c in 0..amt) {
print("${table[r][c]} ")
}
println("")
}
}
Using 999999999 as the maxValue instead
Works as expected.
fun main() {
println("Min count: ${coinChange(intArrayOf(2), 3)}")
}
lateinit var table: Array<IntArray>
lateinit var mCoins: IntArray
private val maxValue = 999999999
fun coinChange(coins: IntArray, amt: Int): Int {
table = Array(coins.size, { IntArray(amt + 1) })
mCoins = coins
coins.sort()
buildMinCounts(amt)
val minCount = table[coins.size - 1][amt]
return if (minCount == maxValue) -1 else minCount
}
fun buildMinCounts(amt: Int) {
for (r in 0..mCoins.size - 1) {
for (c in 0..amt) {
val currentDenomValue = mCoins.get(r)
val currentDenomMin = getDenomMin(r, c - currentDenomValue) + 1
val previousDenomMin = getDenomMin(r - 1, c)
if (c == 0) {
table[r][c] = 0
} else table[r][c] = Math.min(currentDenomMin, previousDenomMin)
}
}
}
fun getDenomMin(r: Int, c: Int): Int {
if (r < 0 || c < 0) return maxValue
else return table[r][c]
}
fun printT(amt: Int) {
for (r in 0..mCoins.size - 1) {
for (c in 0..amt) {
print("${table[r][c]} ")
}
println("")
}
}
It's because of overflow. getDenomMin(r, c - currentDenomValue) + 1 returns Integer.MAX_VALUE + 1 which causes overflow. There are two ways to avoid this:
Change maxValue to something such that it doesn't overflows and is actually is the maximum. For example, you have array of size 10^5 containing integers between 1 and 10^9. Now maximum possible sum will 10^5 * 10^9 which is 10^14 so we can set maxValue to any value greater than or equal to 10^14. In your case you can set it to something like 10^5 because you need count not sum which can be at max number of coins available.
val currentDenomMin = getDenomMin(r, c - currentDenomValue) + 1 Before adding 1 you can type it to Long so that it doesn't overflow.
val currentDenomMin = getDenomMin(r, c - currentDenomValue).toLong + 1

Kotlin Exposed Sum on condition (CaseWhenElse)

I have a table with data structure like :
id type cityName regDate
1249 0 City1 2019-10-01
I want to get result output of unique cities and the number of registrations in it per month as a list of data class objects of
data class NewClientsNumberCityMonth(
val cityName: String = "",
val januaryNewClientsNumber :Int = 0,
val februaryNewClientsNumber :Int = 0,
val marchNewClientsNumber :Int = 0,
val aprilNewClientsNumber :Int = 0,
val mayNewClientsNumber :Int = 0,
val juneNewClientsNumber :Int = 0,
val julyNewClientsNumber :Int = 0,
val augustNewClientsNumber :Int = 0,
val septemberNewClientsNumber :Int = 0,
val octoberNewClientsNumber :Int = 0,
val novemberNewClientsNumber :Int = 0,
val decemberNewClientsNumber :Int = 0
val total :Int = 0
)
and use this objects as the strings to fill the table (we dont know the number of unique cities),
result should be like :
City1 5 8 3 1 2 1 4 1 2 1 0 0
City2 69 23 7 5 3 10 24 14 12 23 25 10
...
im trying this
val tempMutList = mutableListOf<NewClientsNumberCityMonthModel>()
transaction(Connection.TRANSACTION_SERIALIZABLE, 2) {
addLogger(StdOutSqlLogger)
ClientsDataExposed
.slice(ClientsDataExposed.cityName)
.selectAll()
.groupBy(ClientsDataExposed.cityName)
.map { it[ClientsDataExposed.cityName] }
.toList().forEach {
val regsInCity = ClientsDataExposed
.slice(
ClientsDataExposed.id,
ClientsDataExposed.cityName,
ClientsDataExposed.type,
ClientsDataExposed.regDate,
ClientsDataExposed.regDate.month()
)
.selectAll()
.andWhere { ClientsDataExposed.cityName.eq(it) }
.andWhere {
ClientsDataExposed.regDate.between(
Date.valueOf("2019-01-01".toString()),
Date.valueOf("2020-01-01".toString())
)
}
.andWhere {
(ClientsDataExposed.type.inList(contracterTypeSelectedCheckboxes.filterValues { it }.keys.toList())) and (ClientsDataExposed.cityName.inList(
citiesSelectedChekboxes.filterValues { it }.keys.toList()
))
}
.map { it[ClientsDataExposed.regDate.month()] }
.toList()
val cityClientsPerMonth = NewClientsNumberCityMonthModel(
it,
regsInCity.count { it == 1 },
regsInCity.count { it == 2 },
regsInCity.count { it == 3 },
regsInCity.count { it == 4 },
regsInCity.count { it == 5 },
regsInCity.count { it == 6 },
regsInCity.count { it == 7 },
regsInCity.count { it == 8 },
regsInCity.count { it == 9 },
regsInCity.count { it == 10 },
regsInCity.count { it == 11 },
regsInCity.count { it == 12 },
regsInCity.count()
)
tempMutList.add(cityClientsPerMonth)
//obj of dataclass
}
viewTableTabOfnewClientsNumberCityMonth.value = tempMutList.map { it }
}
i know, that i should use Sum() and CaseWhenElse , like here , with check on
ClientsDataExposed.regDate.month()
which gives a month number (1-12), to assign the sum() result to my data class property, but i can't find any examples of CaseWhenElse syntax and i can't figure out it myself,
or may be there is another way of getting it whithout using of CaseWhenElse ?
Please check that code:
val monthExpr = ClientsDataExposed.regDate.month()
fun sumByMonth(monthNum: Int) = Expression.build {
val caseExpr = case().
When(monthExpr eq intLiteral(monthNum), intLiteral(1)).
Else(intLiteral(0))
Sum(caseExpr, IntegerColumnType())
}
val janSumExp = sumByMonth(1)
val febSumExp = sumByMonth(2)
ClientsDataExposed.
slice(ClientsDataExposed.cityName, janSumExp, febSumExp).
selectAll().
groupBy(ClientsDataExposed.id).map {
val janSum = it[janSumExp]
val febSum = it[febSumExp]
...
}

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 to display values from the backing fields

i would liek to run the simple example mentioned below. eclipse generates an error says:
main class cant be found or loaded
please let me know how to fix this error and why it happens
in the below code I am trying to use the backing fields. however, the way they are used in the code does not provide the expected output.
please refer to the output section.
how to display output of the backing fields
code:
fun main(args: Array<String>) {
println("Hello, World!")
val p1 = Person_1("jack", 21);
p1.lastName = "stephan"
p1.month = "may"
println("lastName is ${p1.getLastName}")
println("month is ${p1.getMonth}")
val p2 = Person_1("jack", 21);
p2.lastName = "knauth"
p2.month = "june"
println(p2.getLastName)
println(p2.getMonth)
class Person_1 (val name: String, val age : Int) {
//backing field 1
var lastName : String? = null
set(value) {
if (value?.length == 0) throw IllegalArgumentException("negative values are not allowed")
field = value
}
val getLastName
get() = {
lastName
}
//backing field 2
var month : String? = null
set(value) {
field = value
}
val getMonth
get() = {
month
}
}
output:
Hello, World!
lastName is () -> kotlin.String?
month is () -> kotlin.String?
() -> kotlin.String?
() -> kotlin.String?
You can just get rid of your getters like this:
class Person_1 (val name: String, val age : Int) {
//backing field 1
var lastName : String? = null
set(value) {
if (value?.length == 0) throw IllegalArgumentException("negative values are not allowed")
field = value
}
//backing field 2
var month : String? = null
set(value) {
field = value
}
}
If later you'll need them you can add it like this without api changes:
var lastName : String? = null
get() = field
set(value) {
if (value?.length == 0) throw IllegalArgumentException("negative values are not allowed")
field = value
}