Increment an integer Map value in Kotlin

Viewed 1300

Given a Map or MutableMap:

val scores: MutableMap<String, Int> = mutableMapOf(
        "some person" to 0,
        "some other person" to 0,
        "you" to 0,
        "me" to 0
)

I am unable to increment these as I would in Python, and I'm not sure what the proper way to do it is, or if it is even possible.

fun test() {
    scores["you"] += 2
}

This gives the error:

Operator call corresponds to a dot-qualified call 'scores["you"].plusAssign(2)' which is not allowed on a nullable receiver 'scores["you"]'.

I'm not sure what's going on here.

2 Answers

The += operator is a shorthand operator for the plusAssign function (doc), and is used for adding or replacing entries to a mutable map or collection, as specified by the Kotlin doc for map operations

You can also add new entries to maps using the shorthand operator form. There are two ways:

  • plusAssign (+=) operator.
  • the [] operator alias for set().

When you use scores["you"] += 2, since += is a shorthand operator for plusAssign, and scores["you"] can return a nullable value (as can any get operation on a map), you get the compilation error

Operator call corresponds to a dot-qualified call 'scores["you"].plusAssign(2)' which is not allowed on a nullable receiver 'scores["you"]'.

For your use-case, you can better use the merge method (doc) on the map as below

scores.merge("you", 2, Int::plus)

Do note that merge would add an entry to the map even if the key is absent in the map. In case you want to increment the value for the key only if it's present in the map, you can use the computeIfPresent method (doc) as below

scores.computeIfPresent("you"){ _, v -> v + 2 }

The top answer contains false information. It does not always look for the plusAssign operator, and you can use indexing along with += in some cases.

According to the actual doc, it first looks for plusAssign, and then uses it if there is no plus operator function available, if there is no plusAssign available, it tries to generate code like: a = a + b.

If you were to use an ArrayList, it can work, for example:

val arr = arrayListOf(1, 2, 3, 4)
arr[0] += 4  // arr[0] = arr[0] + 4 -> arr.set(0, arr.get(0) + 4)
arr // 5, 2, 3, 4

The only problem is that map[key] returns a null reference when the key does not map to a value in the map. So here's another way of doing it, without a dot qualified call:

val map = hashMapOf(1 to 2, 3 to 4)
// map += 1 to map[1]!! + 2 // If you are sure about the key 
map += 1 to ((map[1] ?: 0) + 2) // map.put(1, (map.get(1) ?: 0) + 2)
map // { 1: 4, 3: 4 }

You don't even need to use the merge method, simply add an extension function:

operator fun Int?.plus(other: Int) = this?.plus(other) ?: other
fun main() {
    val map = hashMapOf(1 to 2, 3 to 4)
    map[1] += 2 // map[1] = map[1] + 2 -> map.put(1, map.get(1).plus(2))
}
Related