Reputation: 17382
I'm trying my hands on Kotlin. Being from a Python background is really giving me a tough time to get the knack of the Kotlin syntax. I'm trying to do a simple dictionary (Mutable Map) operation. However, its giving me exceptions.
This is what I tried. Kotlin compiler
Adding the code snippet for reference.
fun main() {
val openActivityMap = mutableMapOf<String, MutableMap<String, Any>>()
val packageName = "amazon"
val currentTime = 23454321234
if(openActivityMap.containsKey(packageName)){
if(openActivityMap[packageName]?.get("isAlreadyApplied")){
if((openActivityMap[packageName]?.get("lastAppliedAt") - currentTime) > 3600){
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
else{
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
}
Upvotes: 3
Views: 8866
Reputation: 37700
I'm a bit late to the party, but I'd like to point out another solution here.
As I commented on the OP, heterogeneous maps with fixed string keys like this are usually better expressed with classes in Kotlin. For instance, in your case, the class for your main map's values could be the following:
data class PackageInfo(
var isAlreadyApplied: Boolean,
var lastAppliedAt: Long,
)
(you could obviously add more properties if need be)
This would save you all the casts on the final values.
Another point I'd like to make is that if you access the value for a key anyway, you don't need to check up front the existence of the key with containsKey
. Maps return null
for keys that are not associated with any value (this is why you need to check for null after getting the value).
The compiler cannot see the correlation between containsKey
and the subsequent get
or []
access. However, it's smart enough to understand a null check if you simply get the value first and then check for null.
This always applies unless you want to tell the difference between keys that aren't in the map and keys that are in the map but associated to null values (which is quite rare).
All in all, I would write something like that:
fun main() {
val openActivityMap = mutableMapOf<String, PackageInfo>()
val packageName = "amazon"
val currentTime = 23454321234
val packageInfo = openActivityMap[packageName]
if (packageInfo != null) { // the key was found and the value is smart cast to non-null in the next block
if (packageInfo.isAlreadyApplied) {
if ((packageInfo.lastAppliedAt - currentTime) > 3600) {
packageInfo.isAlreadyApplied = false
}
} else {
packageInfo.isAlreadyApplied = false
}
}
}
data class PackageInfo(
var isAlreadyApplied: Boolean,
var lastAppliedAt: Long,
)
Upvotes: 4
Reputation: 1
maybe you can try something like this
if (openActivityMap.containsKey(packageName)) {
val packageMap = openActivityMap[packageName]!!
val applyRequired = (packageMap["lastAppliedAt"] as Long - currentTime) > 3600
packageMap["isAlreadyApplied"] = packageMap.containsKey("isAlreadyApplied") && !applyRequired
}
btw. do you really want to have lastAppliedAt
to be in te future? otherewise it will never be > 3600
Upvotes: 0
Reputation: 1011
There's a problem with the 2 statement below
if(openActivityMap[packageName]?.get("isAlreadyApplied"))
if((openActivityMap[packageName]?.get("lastAppliedAt") - currentTime) > 3600)
As we all know, an IF statement requires a boolean value for it's param. The types of both statement are unknown at compilation time as they are of a Generic type, Any. As such,
This would throw compilation errors as the compiler does not know the types to go with. What could be done is to cast to it's proper types.
Solution
openActivityMap[packageName]?.get("isAlreadyApplied") as Boolean ?: false
((openActivityMap[packageName]?.get("lastAppliedAt") as Int ?: 0) - currentTime)
Giving a default value if it's null.
Upvotes: 1
Reputation: 2826
I would recommend writing tests first and working in small increments, but this should fix your compilation issues:
fun main() {
val openActivityMap = mutableMapOf<String, MutableMap<String, Any>>()
val packageName = "amazon"
val currentTime = 23454321234
if(openActivityMap.containsKey(packageName)){
if(openActivityMap[packageName]?.get("isAlreadyApplied") as Boolean){
if((openActivityMap[packageName]?.get("lastAppliedAt") as Long - currentTime) > 3600){
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
else {
openActivityMap[packageName]?.put("isAlreadyApplied", false)
}
}
}
EDIT: Also I prefer to avoid nullable variables and mutable objects in general, but I suppose there's an exception to every rule.
Upvotes: 3
Reputation: 8870
Couldn't you just declare your Map<String, Any>
to return a Boolean
instead of Any
? So,
val openActivityMap = mutableMapOf<String, MutableMap<String, Boolean>>()
It looks like you're trying to use your second Map to store both Booleans and Ints, which is complicating the logic. You'll need to typecast if you decide to approach it without Typing.
Upvotes: 1