31

I have a mutable HashMap and would like to use it like a default-dictionary. The obvious method appears to be to use getOrElse and provide the default value each time as a second value. However this seems a little inelegant in my use case since the default value doesn't change.

var x = HashMap(1 -> "b", 2 -> "a", 3 -> "c")

println(x.getOrElse(4, "_")
println(x.getOrElse(5, "_"))
// And so on...
println(x.getOrElse(10, "_"))

Is there any way to create a HashMap (or similar class) such that attempting to access undefined keys returns a default value set on the creation of the HashMap? I notice that HashMap.default is just set to throw an exception but I wonder if this can be changed...

1
  • 5
    To anyone who stumbles upon this question, missingfaktor's post correctly states that "Scala 2.9.1. mutable.Map comes with a withDefaultValue method" Commented Jun 27, 2012 at 23:10

4 Answers 4

72

Wow, I happened to visit this thread exactly one year after I posted my last answer here. :-)

Scala 2.9.1. mutable.Map comes with a withDefaultValue method. REPL session:

scala> import collection.mutable
import collection.mutable

scala> mutable.Map[Int, String]().withDefaultValue("")
res18: scala.collection.mutable.Map[Int,String] = Map()

scala> res18(3)
res19: String = ""
Sign up to request clarification or add additional context in comments.

2 Comments

I'm concerned about the performance for this - does mutable.Map use HashMaps? How's its performance?
WithDefault only overrides the default method (thus only affects apply) and returns the default value when the key is not found. Everything else is sent to the underlying Map
36

Try this:

import collection.mutable.HashMap
val x = new HashMap[Int,String]()  { override def default(key:Int) = "-" }
x += (1 -> "b", 2 -> "a", 3 -> "c")

Then:

scala> x(1)
res7: String = b

scala> x(2)
res8: String = a

scala> x(3)
res9: String = c

scala> x(4)
res10: String = -

5 Comments

+1. Wonder why mutable.HashMap doesn't provide a withDefaultValue method like immutable.HashMap.
It's horrible syntax, but it does the trick. Maybe the lack of withDefaultValue for mutable HashMaps should be reported as a bug?
There is a "!!! todo: move to general maps?" comment in the neighboring withDefault in scala-lang.org/api/current/scala/collection/immutable/Map.html. So may be this is not too much of a stretch. The scala-user mailing list is probably a good place to inquire about this and see if a bug should be reported.
@PythonPower an enhancement ticket was submitted lampsvn.epfl.ch/trac/scala/ticket/3829.
Thanks for reporting it! It's really nice to see Scala being so actively improved.
1
scala> val x = HashMap(1 -> "b", 2 -> "a", 3 -> "c").withDefaultValue("-")
x: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,b), (2,a), (3,c))

scala> x(3)
res0: java.lang.String = c

scala> x(5)
res1: java.lang.String = -

EDIT:

For mutable.HashMap, you could do the following:

scala> import collection.mutable
import collection.mutable

scala> val x = new mutable.HashMap[Int, String] {
     |   override def apply(key: Int) = super.get(key) getOrElse "-"
     | }
x: scala.collection.mutable.HashMap[Int,String] = Map()

scala> x += (1 -> "a", 2 -> "b", 3 -> "c")
res9: x.type = Map((2,b), (1,a), (3,c))

scala> x(2)
res10: String = b

scala> x(4)
res11: String = -

There might be a better way to do this. Wait for others to respond.

2 Comments

@PythonPower: You had not specified it in your original question that you were using mutable.HashMap. I don't know how to do it with mutable.HashMap. I'll have to look into the API.
Yes, sorry I didn't specify -- I've edited my question to make it clear now.
0

I'm more a java guy... but if getOrElse is not final, why don't you just extend HasMap and provide something like this:

override def getOrElse(k: Int, default: String) = {
  return super.getOrElse(k,"_")
}

Note: syntax is probably screwed up but hopefully you'll get the point

2 Comments

Yes, I think it is possible to extend HashMap. In which case it'd probably be easier to change the HashMap.default method to return what I want. The constructor could take the default value and the HashMap.default method would just return that. I'm hoping there's an easier method, however.
We don't do this bc it is more complicated than getOrElse - it has less flexibility, is unnecessarily verbose, and if you must do this, is a strong indication you should be using some high level abstraction, like monoids.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.