The problem with a missing key
A missing key often just means "none yet". A shop with no plums has 0 plums. But stock("plums") can't say that: when the key isn't in the map, it throws a java.util.NoSuchElementException with the message key not found: plums.
getOrElse
getOrElse(key, default) takes a key and a default. It gives the key's value when the key is there, and the default when it isn't.
val stock = scala.collection.mutable.Map("apples" -> 5)
println(stock.getOrElse("apples", 0)) // 5
println(stock.getOrElse("plums", 0)) // 0
The default can be any value of a suitable type. For counts it is usually 0; for text it might be "unknown". If you pass text as the default for a map of numbers, Scala accepts it and gives back a value of the common type of the two, which is rarely what you want, so keep the default the same type as the values.
Reading does not add the key
getOrElse only reads. After the two calls above, stock.size is still 1 and stock.contains("plums") is still false. The default is handed back, but it is not stored. To store a value you assign it, as in stock("plums") = 3. There is also getOrElseUpdate(key, default), which does store the default when the key is missing: on a new map with one key, getOrElseUpdate("b", 7) returns 7 and the size becomes 2. Use it only when you want that effect.
How it relates to get and Option
get(key) returns an Option: Some(value) or None. getOrElse on the map does the same job in one step by unwrapping that Option and falling back to your default.
When to use which
Use map(key) when the key must be there and a missing key would be a bug: the exception tells you quickly. Use getOrElse when a missing key is a normal case with a sensible default, like a count of zero. Use contains when the two cases need different code, not just a different number.
A common use is counting: read the current count with getOrElse(word, 0), add one, and store it back under the same key. The first time a word appears it starts from 0.
The task
stock counts the fruit in a shop. The starter code reads and prints the pears. There are no plums, so the task reads "plums" with getOrElse and 0 as the default, stores the result in plums, and prints a plums line the same way as the pears line.
- Write
val plums = stock.getOrElse("plums", 0). The key is missing, soplumsis 0. - Copy the pears
println, withplumsin place ofpears.
val stock = scala.collection.mutable.Map("apples" -> 5, "pears" -> 2)
val pears = stock("pears")
println("We have " + pears + " pears")
val plums = stock.getOrElse("plums", 0)
println("We have " + plums + " plums")
It prints:
We have 2 pears
We have 0 plums
The pears line can use stock("pears") because that key is known to exist. For plums, the key is not there, so getOrElse is the right call.
Key takeaways
map(key)throwsNoSuchElementExceptionfor a missing key.map.getOrElse(key, default)gives the value or the default and never throws.- Reading with
getOrElseleaves the map unchanged. - Choose a default that means "none" for your data, such as 0 for a count.
