5

If I have optionA, and optionB, how can I return optionA if both are defined and optionB is both are not defined.

Basically, I am trying to rewrite this

val result: Option[Long] = 
  if(optionA.isDefined && optionB.isDefined)
     optionA
  else
     optionB

No, optionA.orElse(optionB) is not the same and broke our test cases. BOTH options must be defined and we want to use optionA. IF optionA is defined and optionB is not defined, we want None.

Someone marked my question as a duplicate which it is not. I was having trouble but finally stumbled upon the answer....

ok, I think I got it and I definitely think it is less human readable

optionA.flatMap { aId =>
    optionB.map(bId => bId).orElse(Some(aId))
}

for added clarity. our truth table is thus

optionA.isDefined  optionB.isDefined   resultNeeded
    None               None               None
    Some(a)            None               None
    None               Some(b)            Some(b)
    Some(a)            Some(b)            Some(a)

thanks

Dean Hiller
  • 19,235
  • 25
  • 129
  • 212
  • 1
    Your `flatMap` solution does not what you want. It returns `None`, if `optionA.isEmpty`. But you want `optionB` in this case. – gzm0 Jun 09 '15 at 19:45
  • 1
    `optionB.flatMap(_ => optionA).orElse(optionB)` does what you want. – gzm0 Jun 09 '15 at 19:46
  • 2
    @DeanHiller : I can't answer right now, but to me it seems that matching on `(optionA, optionB)` would be the easiest to read. – Marth Jun 09 '15 at 19:47
  • @gzm0 yes, if optionA is empty WE WANT to return None. using optionA when optionB is None results in a bug in our system...they don't return a valid optionA in that one case(another system has a bug we have to avoid) – Dean Hiller Jun 09 '15 at 19:51
  • (optionA,optionB) match { case (Some(v1) , Some(v2)) => Some(v1) case (_ , a:Option) => a } – Soroosh Sarabadani Jun 09 '15 at 19:54
  • @Marth yes, I really like that idea!!!!! – Dean Hiller Jun 09 '15 at 19:56
  • 1
    @Marth, post the answer as an answer and I will upvote/checkmark it. gzm0's is interesting but I Think it is still confusing though it's better than mine. – Dean Hiller Jun 09 '15 at 19:58

4 Answers4

9

You can use pattern matching :

(optionA, optionB) match {
  case (Some(_), Some(_)) => optionA
  case _ => optionB
}
Marth
  • 23,920
  • 3
  • 60
  • 72
8

Your truth table implies that you only return Some when optionB is defined, so we can start our evaluation with mapping over that option. Inside the map function we know we have a b. Then we check if we have an a too and just return that, otherwise just use our b.

optionB.map(b => optionA.getOrElse(b))
Karl
  • 1,220
  • 9
  • 16
6

I think the cleanest way to express what you want is this, if you're familiar with standard monadic operations:

optionB.flatMap(_ => optionA orElse optionB)

But very clear--and very fast because it avoids any object creation!--would be just to say in logic exactly what you want, i.e., what you already wrote:

if (optionA.isDefined && optionB.isDefined) optionA else optionB

That's what you said ("if both are defined, I want A, else I want B")--so just write it out as code.

Using higher-level methods is not always the way to go when it reduces both clarity and speed.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
3

Try this:

(optionA,optionB) match {
   case (Some(v1) , Some(v2)) => Some(v1)
   case (_ , a:Option) => a
}