Say I am using a match expression to test a value that may or may not be in a Map object as follows:
map.get(key) match {
case Some(value) if (cond1(value)) => res1(value)
case Some(value) if (cond2(value)) => res2()
case None => res2()
case _ => // None of the above
}
As you can see I want to call the res2 in either case where I have a value for my key and it meets condition 2 or I have no value for key. Can anyone suggest a better construct that would avoid the duplicate calls to res2() in the sample above?
Thanks
Des
* Sorry I realised that the code sample was not quite correct and have updated accordingly. I only want to call res2 in the case where the value for the key meets cond2 OR there is NO entry for key.
I believe what you want is:
map.get(key) match {
case Some(value) if (cond1(value)) => res1(value)
case o: Option[String] if ( o.forall(cond2) ) => res2()
case _ =>
}
replacing [String] with whatever the type of key is.
The names of the methods on Option aren't always the most obvious; in maintaining functional purity they sacrifice clarity for us illiterati. In this case, the scaladoc for Option tells us that forall:
Returns true if this option is empty or the predicate p returns true
when applied to this scala.Option's value.
Without gard if that are only possibilities:
case Some(value) => if (cond1(value) ) res1(value) else res2()
Related
I want to compute something if exactly one of two options is non-empty. Obviously this could be done by a pattern match, but is there some better way?
(o1, o2) match {
case (Some(o), None) => Some(compute(o))
case (None, Some(o)) => Some(compute(o))
case _ => None
}
You could do something like this:
if (o1.isEmpty ^ o2.isEmpty)
List(o1,o2).flatMap(_.map(x=>Some(compute(x)))).head
else
None
But pattern matching is probably the better way to go.
Thanks to helpful comments from #Suma, I came up with another solutions in addition to the current ones:
Since the inputs are always in the form of Option(x):
Iterator(Seq(o1,o2).filter(_!=None))
.takeWhile(_.length==1)
.map( x => compute(x.head.get))
.toSeq.headOption
Using iterator also allows for a sequence of values to be passed to the input. The final mapping will be done if and only if one value in the sequence is defined.
Inspired by now deleted answer of pedrofurla, which was attempting to use o1 orElse o2 map { compute }, one possibility is to define xorElse, the rest is easy with it:
implicit class XorElse[T](o1: Option[T]) {
def xorElse[A >: T](o2: Option[A]): Option[A] = {
if (o1.isDefined != o2.isDefined) o1 orElse o2
else None
}
}
(o1 xorElse o2).map(compute)
Another possibility I have found is using a pattern match, but using Seq concatenation so that both cases are handled with the same code. The advantage of this approach is it can be extended to any number of options, it will always evaluate when there is exactly one:
o1.toSeq ++ o2 match {
case Seq(one) => Some(compute(one))
case _ => None
}
Just initialize a sequence and then flatten
Seq(o1, o2).flatten match {
case Seq(o) => Some(compute(o))
case _ => None
}
I have been working with Scala for close to a year, but every now and then I come across a piece of code that I don't really understand. This time it is this one. I tried looking into documents on "scala methods with generic parameter type", but I am still confused.
def defaultCall[T](featureName : String) (block : => Option[T])(implicit name: String, list:Seq[String]) : Option[T] =
{
val value = block match {
case Some(n) => n match {
case i : Integer => /*-------Call another method----*/
case s : String => /*--------Call another method----*/
}
case _ => None
}
The method is called using the code shown below :
var exValue = Some(10)
val intialization = defaultCall[Integer]("StringName"){exValue}
What I don't understand in the above described code is the "case" statement in the defaultCall method.
I see that when the exValue has a value and is not empty, the code works as expected. But in case I change the exValue to None, then my code goes into the "case _ = None" condition. I don't understand why this happens since the match done here is against the "variable" which would be either an Integer or a String.
What happens here is that when you pass a None it will match on the second case, which "catches" everything that is not an instance of a Some[T]:
block match {
case Some(n) => // Will match when you pass an instance of Some[T]
case _ => // Will match on any other case
}
Note that None and Some are two different classes that inherit from Option.
Also, the variable match is only done if the first match succeeds, otherwise not. To achieve the type checking in the first match you could do:
block match {
case Some(n: Int) => // do stuff
case Some(n: String) => // do stuff
case _ => // Will match on any other case
}
Hope that helps
This code compiles fine, but fails at runtime:
val values = Set("a").toSeq.sorted
values match {
case Nil => println("empty")
case h::t => println(s"h = $h")
}
With the error message:
scala.MatchError: ArrayBuffer(a) (of class scala.collection.mutable.ArrayBuffer)
I understand that somewhere in the process an ArrayBuffer is created, on which I cannot pattern-match like this. However, why can't the compiler tell me that this is not going to work?
You are matching on an open (extensible) data type, Scala's Seq. It could be a List, so Scala doesn't complain with your List pattern. On the other hand, exhaustiveness cannot be checked because it could really be any class implementing Seq (that we may not even know statically), so Scala just trusts you on this one.
You can use generic Seq patterns instead:
values match {
case Seq() => println("empty")
case h +: t => println(s"h = $h")
}
Or just convert to a List and use the same patterns (but Lists are not very efficient data structures, so it's probably better with the first option).
val values = Set("a").toList.sorted
values match {
case Nil => println("empty")
case h::t => println(s"h = $h")
}
Getting errors from pattern matching can be tricky sometimes, but in this case:
scala> :type values
Seq[String]
scala> Seq(1,2,3) match { case h::t => "ok" }
res1: String = ok
There's not enough type info to say it can't work, and it errs on the side of not annoying you.
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
You can use pattern matching :
(optionA, optionB) match {
case (Some(_), Some(_)) => optionA
case _ => optionB
}
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))
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.
Try this:
(optionA,optionB) match {
case (Some(v1) , Some(v2)) => Some(v1)
case (_ , a:Option) => a
}
like similar question: Convert match statement to partial function when foreach is used. Now similarly, IntelliJ asks me to improve my code. The difference is, that I use values for the matching:
val matchMe = "Foo"
keys.foreach(key =>
key match {
case `matchMe` => somethingSpecial()
case _ => somethingNormal(key, calcWith(key))
})
Refactoring this to a anonymous pattern-matching function would look something like:
keys.foreach {
case `matchMe` => somethingSpecial(_)
case _ => somethingNormal(_, calcWith(_)) //this doesn't work
}
Note that in the second case, I cannot use _ since I need it twice. Is there some way to use an anonymous pattern-matching function here?
You can't use the wildcard _ here, its purpose is to indicate you don't care about the value you're matching against.
You can use a named parameter :
keys.foreach {
case `matchMe` => somethingSpecial(matchMe)
case nonSpecialKey => somethingNormal(nonSpecialKey, calcWith(nonSpecialKey))
}
Without any restrictions placed on it, it will match any value. Do note that the order of cases is important, as case x => ... match anything and will essentially shortcut other case statements.
As an aside, I don't think your somethingSpecial(_) does what you want/expect it to. It's only a short version of x => somethingSpecial(x), not somethingSpecial(matchMe).