Type mismatch error when adding a map value to a variable (Kotlin) - kotlin

I want to add a value from a MutableMap to the total, but I keep getting Type mismatch: inferred type is Int? but Int was expected error and I don't know how to fix this issue
I tried calling the value as Int menu[item].toInt() and setting logic in the if statement that checks that the item is indeed Int, but nothing helped.
Please, see the code below
fun main() {
val order = Order()
order.placeOrder("Noodles")
}
val menu: MutableMap<String, Int> = mutableMapOf("Noodles" to 10,
"Vegetables Chef's Choice" to 5,
)
class Order {
var total = 0
fun placeOrder(vararg orderedItems: String) {
for (item in orderedItems) {
if (item in menu) {
total += menu[item]
}
}
}
}

Your call to menu[item] is a shorthand for menu.get(item) which is defined as Map<K, out V>.get(key: K): V?.
In case no element with the specified key is found in the map, null is returned.
You may fix this by providing a default value, in case the returned value is null, e.g.:
total += menu[item] ?: 0
Map<K, out V> also provides alternatives, which provide Int instead of Int?.
You may use getValue(key: K): V, which throws an NoSuchElementException, when there is no element with the given key.
Alternatively, you can utilize getOrElse(key: K, defaultValue: () -> V): V or getOrDefault(key: K, defaultValue: V): V which both provide a fallback value instead of null.
Examples for the different alternatives below:
total += menu.getValue(item)
total += menu.getOrElse(item) { 0 }
total += menu.getOrDefault(item, 0)

Related

Why am I forced to make the below functional property nullable in Kotlin?

package algorithms
import algorithms.util.IOUtils
object Calculator {
/* static abstract class Operation {
* private Function logic; //functional interface
* Operation(Function f) { this.logic = f );
* }
*/
sealed class Operation(val logic : (Int, Int)-> Int)
/* define singleton types 'ADD', 'SUB', 'MUL', 'DIV' - equivalent to inner class definitions in Java */
/* static class ADD extends Operation {
* ADD(){
* super((int x, int y)-> x+y);
* }
* }
*/
object ADD: Operation({ x: Int , y: Int -> x+y } )
object SUB: Operation({ x: Int , y: Int -> x-y })
object MUL: Operation({ x: Int , y: Int -> x*y })
object DIV: Operation({ x: Int , y: Int -> x/y })
private fun getOperationFromChar(ch : Char): Operation? {
return when(ch){
'+' -> ADD
'-' -> SUB
'*' -> MUL
'/' -> DIV
else -> null
}
}
fun eval(ch: Char, x: Int, y: Int): Int? {
val op : Operation? = getOperationFromChar(ch)
return op?.logic?.invoke(x,y)
}
}
fun main(){
println("Result : ${Calculator.eval(
IOUtils.readChar("Enter desired operation (+,-,*,/) "),
IOUtils.readInteger("Enter first number"),
IOUtils.readInteger("Enter second number"))}")
}
The above code works fine, however, IntelliJ forces me to make logic in
return op?.logic?.invoke(x,y) nullable
Although the definition of Operation sealed class Operation(val logic : (Int, Int)-> Int) has nowhere mentioned that it can be null.
I would image if the definition of the Operation object was sealed class Operation(val logic : ((Int, Int)-> Int)?) then it would make sense, but it is not so.
What is going on here?
It's because the return value of getOperationFromChar() is nullable.
It's not your operation function that returns a nullable value. op itself is already nullable. You defined it yourself with val operation: Operation?. When you use ?. calls, the results are always nullable because null will be the result if there was no object to call the function on.
The input of your getOperationFromChar() function is a Char. A Char can be any of many thousands of possible values, not just the four that you have in your when statement. That's why the compiler is enforcing an else branch. If you want to avoid returning a nullable, you could choose to throw an error if an invalid input is given:
private fun getOperationFromChar(ch : Char): Operation {
return when(ch){
'+' -> ADD
'-' -> SUB
'*' -> MUL
'/' -> DIV
else -> error("Invalid input $ch")
}
}
Then you could define val op: Operation and it would be able to accept the result of this function as non-nullable.
Sealed classes help avoid the need for an else branch when the sealed class is the type of the when's subject. Then the compiler can be sure you have a branch for every possible input. Your function is the opposite case, where your sealed class type is the output, not the input.
By the way, it is more sensible for you to use an enum instead of a sealed class for this case, because none of the children of your sealed class have unique properties or functions.
If you take the chained evaluation apart, it becomes clear:
fun eval(ch: Char, x: Int, y: Int): Int? {
val op: Operation? = getOperationFromChar(ch)
val logic: ((Int, Int) -> Int)? = op?.logic
val retval: Int? = logic?.invoke(x, y)
return retval
}
logic is not typed ((Int, Int) -> Int) but ((Int, Int) -> Int)?, because if op is null, the result of op?.logic will also be null.
It is because op is nullable; if you set type of op as Operation you don't need to check the nullability of logic.
In fact, it checks the nullability of whole op?.logic to call invoke() method; that can throw NullPointerException due to op nullability.
The safe call operator(?.) returns null if the value to the left is null, otherwise continues to evaluate the expression to the right.
for example
val x:Int? = 4
x?.dec()?.inc()?.dec()
x?.let {
it.dec().inc().dec()
}

How to use comparator in priority queue kotlin

Hey I want to use custom comparator in priority queue in kotlin. I have data class
Product.kt
data class Product(val value: String? = null, val price: String? = null) {
var priceInLong = price?.toLong()
}
I want to create a min heap where price value will be minimum. I am creating the object but it giving me some kind of error
fun main() {
var queue = PriorityQueue<Long> { p1: Product, p2: Product ->
p1.priceInLong?.let {
p2.priceInLong?.minus(it)
}
}
val list = listOf(
Product("1", "4.83"),
Product("2", "4.53"),
Product("3", "3.54"),
Product("4", "3.66"),
Product("5", "5.16")
)
}
error
None of the following functions can be called with the arguments supplied.
<init>((MutableCollection<out TypeVariable(E)!>..Collection<TypeVariable(E)!>?))   where E = TypeVariable(E) for   constructor PriorityQueue<E : Any!>(c: (MutableCollection<out E!>..Collection<E!>?)) defined in java.util.PriorityQueue
<init>(Comparator<in TypeVariable(E)!>!)   where E = TypeVariable(E) for   constructor PriorityQueue<E : Any!>(comparator: Comparator<in E!>!) defined in java.util.PriorityQueue
<init>(PriorityQueue<out TypeVariable(E)!>!)   where E = TypeVariable(E) for   constructor PriorityQueue<E : Any!>(c: PriorityQueue<out E!>!) defined in java.util.PriorityQueue
<init>(SortedSet<out TypeVariable(E)!>!)   where E = TypeVariable(E) for   constructor PriorityQueue<E : Any!>(c: SortedSet<out E!>!) defined in java.util.PriorityQueue
<init>(Int)   where E = TypeVariable(E) for   constructor PriorityQueue<E : Any!>(initialCapacity: Int) defined in java.util.PriorityQueue
image
1. I want to solve this error and add value by price which is minimum comes first.
2. Is my above queue comparator logic is correct to use min heap?
Thankss
UPDATE
I tried this suggestion
var queue = PriorityQueue<Product> { p1, p2 ->
return if (p1.priceInLong != null && p2.priceInLong != null) {
p2.priceInLong - p1.priceInLong
} else {
0
}
}
getting error
UPDATE 2
val queue = PriorityQueue<Product> { p1, p2 ->
val priceOne = p1.priceInLong
val priceTwo = p2.priceInLong
if (priceOne != null && priceTwo != null) {
if(priceOne == priceTwo){
return 0
}
} else {
return 0
}
}
data class Product(val value: String? = null, val price: String? = null) {
val priceInLong = price?.toLong()
}
This:
{ p1: Product, p2: Product ->
p1.priceInLong?.let {
p2.priceInLong?.minus(it)
}
}
returns null if p1.priceInLong is null (the let block isn't executed), or if p2.priceInLong is null (the let block returns null). That's what the null-safety checking with ? does.
You're getting the error because your Comparator function needs to return an Int, but yours returns Int?, i.e. a nullable int value - could be an int, could be null. So it doesn't match the required constructor, that's why it's complaining that none of the functions match.
So you need to decide what to do if one (or both) of those values are null, and return an integer instead of any nulls, so your function returns Int and not Int?
If what you're saying in the comments is correct, and neither of those values will ever be null, you can just assert that with !!
{ p1: Product, p2: Product ->
p2.priceInLong!! - p1.priceInLong!!
}
But using !! is a bad sign - how do you know they'll never be null? If you can say that for sure, why is priceInLong nullable in the first place?
I want to solve this error
Two problems here:
Type argument of PriorityQueue should be a Product (if you want to store these objects in it).
Lambda of Comparator should return Int, not Long?. If it's guaranteed, that there will be no Products with priceInLong == null, you may just use the not-null assertion operator to get rid of nullability and then get sign of difference to avoid possible integer overflow of .toInt() conversion:
val queue = PriorityQueue<Product> { p1, p2 -> (p1.priceInLong!! - p2.priceInLong!!).sign }

Operator overloading on += for set and get calls wrong setter

I have made an extension functions for BigIntegers, allowing me to add Ints to them.
operator fun BigInteger.plus(other: Int): BigInteger = this + other.toBigInteger()
// Allowing me to do
val c = myBigInt + 3
I have also made a Counter class, holding bigintegers for various keys, for easy counting. Since doing counter["1"] += myBigInt isn't allowed on standard maps (it's nullable), I have added a custom getter that returns a default value, making this possible.
class Counter<K>(val map: MutableMap<K, BigInteger>) : MutableMap<K, BigInteger> by map {
constructor() : this(mutableMapOf())
override operator fun get(key: K): BigInteger {
return map.getOrDefault(key, BigInteger.ZERO)
}
I can then use it like this
val counter = Counter<String>()
c["ones"] += 5.toBigInteger()
Problem is that I cannot use it like this:
c["ones"] += 5 // doesn't work, "Kotlin: No set method providing array access"
but this should be equivalent to this, which works, since it should use my extension operator on the bigint:
c["ones"] = c["ones"] + 5 // works
Why doesn't this work?
I've tried adding a set method for Ints, but then I see a very weird behavior. Kotlin will do the calculation correct, but then convert the BigInteger to an Int before passing it to my class! Example:
inline operator fun BigInteger.plus(other: Int): BigInteger {
val bigInteger = this + other.toBigInteger()
println("calculated bigint to $bigInteger")
return bigInteger
}
class Counter<K>(val map: MutableMap<K, BigInteger>) : MutableMap<K, BigInteger> by map {
constructor() : this(mutableMapOf())
override operator fun get(key: K): BigInteger {
return map.getOrDefault(key, BigInteger.ZERO)
}
operator fun set(key: K, value: Int) {
println("setting int $value")
map[key] = value.toBigInteger()
}
}
val c = Counter<String>()
c["1"] = "2192039569601".toBigInteger()
c["1"] += 5
println("result: ${c["1"]}")
c["1"] = "2192039569601".toBigInteger()
c["1"] = c["1"] + 5
println("result: ${c["1"]}")
Which prints
calculated bigint to 2192039569606
setting int 1606248646 <--- why does it call the int setter here?
result: 1606248646
calculated bigint to 2192039569606
result: 2192039569606
Why does Kotlin do the BigInt summation, but converts it back to an Int before sending to my setter?
Update
Since a comment suggest this is a compiler issue, any other ideas?
My ultimate goal here, was to have a counter of big integers, but to be able to easily add ints to it.
Adding this as a set function, makes it being called for both ints and bigints, so I can do the proper assignment myself. However, it will also then allow someone to add floats that will crash at runtime.
operator fun set(key: K, value: Number) {
map[key] = when (value) {
is BigInteger -> value
is Int -> value.toBigInteger()
else -> throw RuntimeException("only ints")
}
}
Any tips?
Notice that c["ones"] += 5 can be translated into calls in two ways:
c.set("ones", c.get("ones").plus(5))
c.get("ones").plusAssign(5)
The first way is what your code currently translates to, because you don't have a plusAssign operator defined. As I said in the comments, there is a bug in the compiler that prevents the operators from resolved correctly. When resolving c["ones"] += 5, It seems to be trying to find a set operator that takes an Int instead (possibly because 5 is an Int), which is unexpected. If you modify the code in the bug report a little, you can even make it throw an exception when executed!
class Foo {
operator fun get(i: Int) : A = A()
operator fun set(i: Int, a: A) {}
operator fun set(i: Int, a: Int) {}
}
class A {
operator fun plus(b: Int) = A()
}
class B
fun main(args: Array<String>) {
val foo = Foo()
foo[0] = foo[0] + 1
foo[0] += 1 // this compiles now, since there is a set(Int, Int) method
// but A can't be casted to Int, so ClassCastException!
}
It is rather coincidental (and lucky) in your case, that the compiler knows how to convert from BigInteger (or any other Number type actually) to Int, using Number#intValue. Otherwise the program would have crashed too.
A natural alternative way is to define the plusAssign operator, so that the assignment gets translated the second way. However, we can't do it on BigInteger, because plusAssign would need to mutate this, but BigInteger is immutable. This means that we need to create our own mutable wrapper. This does mean that you lose the nice immutability, but this is all I can think of.
fun main() {
val c = Counter<String>()
c.set("1", "2192039569601".toMutableBigInteger())
c.get("1").plusAssign(5)
println("result: ${c["1"]}")
}
data class MutableBigInteger(var bigInt: BigInteger) {
operator fun plusAssign(other: Int) {
bigInt += other.toBigInteger()
}
}
fun String.toMutableBigInteger() = MutableBigInteger(toBigInteger())
class Counter<K>(val map: MutableMap<K, MutableBigInteger>) : MutableMap<K, MutableBigInteger> by map{
constructor() : this(mutableMapOf())
override operator fun get(key: K): MutableBigInteger {
return map.getOrPut(key) { MutableBigInteger(BigInteger.ZERO) }
}
operator fun set(key: K, value: Int) {
println("setting int $value")
map[key] = MutableBigInteger(value.toBigInteger())
}
}
Notably, getOrDefault is changed to getOrPut - when a value is not found, we want to put the zero we return into the map, rather than just returning a zero that is not in the map. Our changes to that instance wouldn't be visible through the map otherwise.

Kotlin - Why do we have to explicit type parameter(s) for generic method?

I'm working on extension method like this:
infix fun <T> T.isNullOr(other: T): Boolean {
if (this == null) return true
return this == other
}
and I'm trying to use this method like this.
val thisShouldWork = true isNullOr true // this is true
val thisShouldNotWork = true isNullOr 0 // No compilation errors?
I expected compilation error because type parameter is automatically set to Boolean for isNullOr but it wasn't. What's happening?
am I misunderstanding about it?
in C#, same code working well as I expected.
static bool IsNullOr<T>(this T t, T other) {
if (t == null) return true;
return Equals(t, other);
}
bool howAboutThis = 0.IsNullOr(0);
bool andThis = 0.IsNullOr(false); // error - cannot detect type parameter for this
Here, val thisShouldNotWork = true isNullOr 0 is equal to val thisShouldNotWork: Boolean = true.isNullOr<Any>(0). Type parameter as inferred as the closest parent.
And function's return type is based on logical expression evaluation: this == other. Let's see == function declaration: public open operator fun equals(other: Any?): Boolean. It receives Any?.
Type parameter in this function has nothing to do with Boolean.
Just remember that generic type information is erased at runtime and whenever you try to put something into a method that accepts generics, then the common denominator is assumed, e.g.:
listOf("one", 123) // -> assumes T:Any and therefore gives List<Any>
Now for your example that would mean "one".isNullOr(123) both become Any.
As a sidenote however, if you declare a specific type (e.g. List<String>) as shown next, it will not work to assign a different type to it:
val test : List<String> = listOf(123) // this will not work
It is already known at compile time that the given int can't become a string. This sample however doesn't help you as you do not return that generic type. If your method just looked a bit different, e.g. would have a generic type as return value, it might easily have worked out similar to the List-sample before.
So to fix your sample you need to specify the type which will basically make the infix obsolete, e.g. the following will work as you expect:
val someString : String? = TODO()
val works = someString.isNullOr<String?>("other")
val doesntWork = someString.isNullOr<Int?>(123) // does not nor does:
val doesntWorkToo = someString.isNullOr<String?>(123)
Note that for what you've shown some standard functionality might help you (but not eliminate that specific problem), i.e. using the ?: (elvis operator) with a ?.let:
val someVal : String? = "someString given from somewhere"
val thisWorks = someVal?.let {
it == "some other string to compare"
} ?: true /* which basically means it was null */
val thisWillNot = someVal?.let {
it == 123 // compile error (funny enough: it.equals(123) would work ;-)
} ?: true /* it is null */
I think in this case the generics don't really matter. You only call equals in the method, which you can do on any type. It's basically the same as:
infix fun Any.isNullOr(other: Any): Boolean {
return this == other
}
It compiles without problems because you can always call equals with anything: other: Any?
Thank for answers. I think there is no way to prevent this at compilation level, so I decided to check type for other.
inline infix fun <reified T> T.isNullOr(other: T): Boolean {
if (this == null) return true
if (other !is T) return false
return this == other
}
If you really want to prevent it, you can:
class IsNullOr<T>(val x: T) {
operator fun invoke(other: T): Boolean {
if (x == null) return true
return x == other
}
}
fun <T> T.isNullOr() = IsNullOr(this)
fun main(args: Array<String>) {
val thisShouldWork = true.isNullOr()(true) // compiles
val thisShouldNotWork = true.isNullOr()(0) // doesn't compile
}
This makes type inference depend only on the receiver of isNullOr. If vals could be generic, you'd even keep the original syntax (but they can't).

Component destructuring with fewer than expected components

Let's say I want to do the following:
val (k, v) = pair.split("=".toRegex(), 2)
This code is fine if I always get 2 components from the split - however, if the delimiter is not present in the string, this code throws an exception, because the second element in the array isn't present.
The answer is almost certainly "no", but is there some way to coerce destructure to assign null values to missing components?
When destructuring objects, Kotlin calls componentN() for that object. For arrays, component1() is equal to get(0), component2() is equal to get(1), and so on.
So if the index is out of bounds, it'll throw ArrayIndexOutOfBoundsException, instead of returning null.
But you can make your operator function like this:
operator fun <T> Array<out T>.component1(): T? = if (size > 0) get(0) else null
operator fun <T> Array<out T>.component2(): T? = if (size > 1) get(1) else null
so if I run
val (k, v) = arrayOf(1)
println(k)
println(v)
the output will be
1
null
See:
Destructuring Declarations
You could add your own extension to List that adds the required number of null values to the end:
val (k, v) = pair.split("=".toRegex(), 2).padWithNulls(limit = 2)
Implementation can be done a couple of ways, here's just one:
private inline fun <reified E> List<E>.padWithNulls(limit: Int): List<E?> {
if (this.size >= limit) {
return this
}
val result: MutableList<E?> = this.toMutableList()
result.addAll(arrayOfNulls(limit - this.size))
return result
}
Here's a simpler one as well:
private fun <E> List<E>.padWithNulls(limit: Int): List<E?> {
val result: MutableList<E?> = this.toMutableList()
while (result.size < limit) {
result.add(null)
}
return result
}
Or wrapping this functionality even further:
val (k, v) = pair.splitAndPadWithNulls("=".toRegex(), 2)
private fun String.splitAndPadWithNulls(regex: Regex, limit: Int): List<String?> {
return this.split(regex, limit).padWithNulls(limit)
}
Its working for me
val pair="your string"
if(pair.isNotEmpty()&&pair.contains("=")) {
val (k, v) = pair.split("=".toRegex(), 2)
println(k)
println(v)
}
It doesn't cover as many cases as other answers (also might not be as obvious what's happening) but you can always force there to be at least the correct number of values to destructure (extra values will be ignored). Using your example you can just add null to increase the size of the list returned by split:
val (k, v) = "foo=bar".split("=".toRegex(), 2) + null
> k=foo, v=bar
val (k, v) = "foo".split("=".toRegex(), 2) + null
> k=foo, v=null
Playground example https://pl.kotl.in/W7gGYyAjC