问题
Is there a way to check if a Map has a defined default value? What I would like is some equivalent of myMap.getOrElse(x, y) where if the key x is not in the map,
- if
myMaphas a default value, return that value - else return
y
A contrived example of the issue:
scala> def f(m: Map[String, String]) = m.getOrElse("hello", "world")
f: (m: Map[String,String])String
scala> val myMap = Map("a" -> "A").withDefaultValue("Z")
myMap: scala.collection.immutable.Map[String,String] = Map(a -> A)
scala> f(myMap)
res0: String = world
In this case, I want res0 to be "Z" instead of "world", because myMap was defined with that as a default value. But getOrElse doesn't work that way.
I could use m.apply instead of m.getOrElse, but the map is not guaranteed to have a default value, so it could throw an exception (I could catch the exception, but this is nonideal).
scala> def f(m: Map[String, String]) = try {
| m("hello")
| } catch {
| case e: java.util.NoSuchElementException => "world"
| }
f: (m: Map[String,String])String
scala> val myMap = Map("a" -> "A").withDefaultValue("Z")
myMap: scala.collection.immutable.Map[String,String] = Map(a -> A)
scala> f(myMap)
res0: String = Z
scala> val mapWithNoDefault = Map("a" -> "A")
mapWithNoDefault: scala.collection.immutable.Map[String,String] = Map(a -> A)
scala> f(mapWithNoDefault)
res1: String = world
The above yields the expected value but seems messy. I can't pattern match and call apply or getOrElse based on whether or not the map had a default value, because the type is the same (scala.collection.immutable.Map[String,String]) regardless of default-ness.
Is there a way to do this that doesn't involve catching exceptions?
回答1:
You can check whether the map is an instance of Map.WithDefault:
implicit class EnrichedMap[K, V](m: Map[K, V]) {
def getOrDefaultOrElse(k: K, v: => V) =
if (m.isInstanceOf[Map.WithDefault[K, V]]) m(k) else m.getOrElse(k, v)
}
And then:
scala> val myMap = Map("a" -> "A").withDefaultValue("Z")
myMap: scala.collection.immutable.Map[String,String] = Map(a -> A)
scala> myMap.getOrDefaultOrElse("hello", "world")
res11: String = Z
scala> val myDefaultlessMap = Map("a" -> "A")
myDefaultlessMap: scala.collection.immutable.Map[String,String] = Map(a -> A)
scala> myDefaultlessMap.getOrDefaultOrElse("hello", "world")
res12: String = world
Whether this kind of reflection is any better than using exceptions for non-exceptional control flow is an open question.
回答2:
You could use Try instead of try/catch, and it would look a little cleaner.
val m = Map(1 -> 2, 3 -> 4)
import scala.util.Try
Try(m(10)).getOrElse(0)
res0: Int = 0
val m = Map(1 -> 2, 3 -> 4).withDefaultValue(100)
Try(m(10)).getOrElse(0)
res1: Int = 100
来源:https://stackoverflow.com/questions/24702250/how-to-tell-if-a-map-has-a-default-value