Reputation: 1217
I understand
e.g.
map.getOrElse("key1","default")
Meanwhile I am interacting with a Java library, which some values are null.
e.g. Map("key1"->null)
getOrElse
will throw null pointer in this case.
I want to handle both cases and result in writing something like this
def getOrElseNoNull[A,B](map:Map[A,B],key:A,default:B) = {
map.get(key) match{
case Some(x) if x != null => x
case _ => default
}
}
which is quite ugly. (it is Map[Any] and I need a string from that key)
getOrElseNoNull(map,"key1","").asInstanceOf[String])
is it possible to use implicit to extend the map, or any other elegant way?
Upvotes: 5
Views: 15221
Reputation: 717
Another simple solution is to wrap map.get result with Scala Option.
val value = Option(map.get(key))
Upvotes: 0
Reputation: 32719
If you're dealing with an immutable Map, the safest thing to do would be to filter out all the null values up front (this incurs the creation of yet another Map instance, but unless you have a specific reason to care about performance here it should not be an issue).
val withoutNulls = map.filter{case (k,v)=> v != null}
Any key that was holding a null is gone, and as such getOrElse
on this key will return None
.
Trivial, and does the job once for all.
Upvotes: 11
Reputation: 478
One possibility is to map the values over Option()
:
val withoutNulls: Map[Int, Option[String]] = withNulls.mapValues(Option.apply)
This gives you the possibility to handle missing values and nulls the same way:
val nullsEqualMissing: Map[Int, Option[String]] = withoutNulls.withDefaultValue(None)
nullsEqualMissing(1).fold{ "nullOrMissing" }{ identity }
Or to handle missing values separately:
withoutNulls.get(1).fold{ "missing" }{ _.fold{ "null" }{ identity }}
Upvotes: 1
Reputation: 1022
There is no real need to create something new, scala supports several methods to achieve this with default values:
// define a function to handle special values
Map("1" -> "2").withDefault( _ => "3").apply("4")
// default values for all unknown values
Map("1" -> "2").withDefaultValue("3").apply("4")
// handle a specific case
Map("1" -> "2").getOrElse("unknown", "3")
Another option is to use the Option to get the null value in a less ugly way:
None.orNull
This will get you the Option value or return null in case of None.
Upvotes: 0
Reputation: 159855
Implicit extension classes to the rescue:
implicit class NullOccludingMap[K, V](private val underlying: Map[K, V]) extends AnyVal {
def getNonNullOrElse(key: K, default: V): V = {
underlying.get(key) match {
case Some(value) if value != null => value
case _ => default
}
}
}
Then you can use it anywhere it is in scope:
val test = Map("x" -> "Hi", "y" -> null)
test.getNonNullOrElse("z", "") // ""
test.getNonNullOrElse("y", "") // ""
Upvotes: 5