Reputation: 2425
Suppose I have an item of the type (Option[Long], Option[Long], Option[Long], Option[Long], Option[Long])
, and I want to convert it to an item of type (Long, Long, Long, Long, Long)
. I want each coordinate to contain the value of the option (if the option contains a "Some" value), or be zero otherwise.
Usually if I have an item of type Option[Long]
, I'd do something like
item match {
case Some(n) => n
case None => 0
}
But I can't do that with a 5 coordinate item unless I want to list out all 32 possibilities. What can I do instead?
Upvotes: 1
Views: 1713
Reputation: 9820
Using Shapeless you could do:
import shapeless._
import syntax.std.tuple._
import poly._
object defaultValue extends Poly1 {
implicit def defaultOptionLong = at[Option[Long]](_.getOrElse(0L))
}
val tuple : (Option[Long], Option[Long], Option[Long], Option[Long], Option[Long]) =
(Some(1L), None, Some(3L), Some(4L), None)
tuple.map(defaultValue)
// (Long, Long, Long, Long, Long) = (1,0,3,4,0)
You need to explicitly specify type Option[Int]
if you don't use Option.apply
(see this question).
(Option(1L), Option(2L)).map(defaultValue)
// (Long, Long) = (1,2)
(Some(3L), Some(4L)).map(defaulValue) // does not compile
val t : (Option[Long], Option[Long]) = (Some(3L), Some(4L))
t.map(defaultValue)
// (Long, Long) = (3,4)
(Option(5), None).map(defaultValue) // does not compile
val t2 (Option[Long], Option[Long]) = (Option(5), None)
t2.map(defaultValue)
// (Long, Long) = (5,0)
We could also provide default values for other types:
object defaultValue extends Poly1 {
implicit def caseLong = at[Option[Long]](_.getOrElse(0L))
implicit def caseInt = at[Option[Int]](_.getOrElse(0))
implicit def caseString = at[Option[String]](_.getOrElse("scala"))
}
val tuple2 : (Option[Int], Option[Long], Option[String]) = (None, None, None)
tuple2.map(defaultValue)
// (Int, Long, String) = (0,0,scala)
Edit: The problem with the need of explicit declaration of Some(5L)
as Option[Long]
can be solved using generics in the poly function :
objec defaultValue extends Poly1 {
implicit def caseLong[L <: Option[Long]] = at[L](_.getOrElse(0L))
implicit def caseInt[I <: Option[Int]] = at[I](_.getOrElse(0))
implicit def caseString[S <: Option[String]] = at[S](_.getOrElse("scala"))
}
(Some("A"), Some(1), None: Option[Int], None: Option[String]).map(defaultValue)
// (String, Int, Int, String) = (A,1,0,scala)
Upvotes: 2
Reputation: 6006
Another possible solution:
item.productIterator.collect{
case Some(a: Int) => a
case _ => 0
}.toList match {
case List(a,b,c,d,e) => (a,b,c,d,e)
case _ => (0,0,0,0,0) //or throw exception depending on your logic
}
Upvotes: 1
Reputation: 7247
Simple solution:
item match {
case (a, b, c, d, e) => (a.getOrElse(0), b.getOrElse(0), c.getOrElse(0), d.getOrElse(0), e.getOrElse(0))
}
Obviously this isn't very generic. For that you'll probably want to look at Shapeless but I'll leave that answer to the resident experts. ;)
Upvotes: 4
Reputation: 53819
You can simply do:
val res = for {
a <- item._1.orElse(0L)
b <- item._2.orElse(0L)
c <- item._3.orElse(0L)
d <- item._4.orElse(0L)
e <- item._5.orElse(0L)
} yield (a, b, c, d, e)
Not the nicest but easy to implement and understand.
Upvotes: 1