How to rewrite complex conditional as pattern matching? - scala

Is there a way to rewrite this conditional as a pattern matching?
val oldEmail : Option[String]
val newEmail : Option[String]
if (newEmail.isDefined && (oldEmail.isEmpty || newEmail.get != oldEmail.get))
sendActivationEmail(newEmail.get)
else
()
my only failed attempt was this:
(newEmail, oldEmail) match {
case (Some(_), None) | (Some(ne), Some(oe)) if ne != oe =>
sendActivationEmail(newEmail.get)
case _ => ()
}
Edit: I should explicitly mention that my goal is a pattern matching with only two case clauses as shown above, for it's learning value

That logic does make for a rather complicated pattern. I'd be tempted to skip pattern matching.
newEmail.collect{
case ne if oldEmail.fold(true)(_ != ne) => sendActivationEmail(ne)
}
Update incorporating worthy input from #Alexey and #Cyrille Corpet and a suggestion from the IntelliJ IDE. (peer review ;-)
newEmail.foreach(ne => if (!oldEmail.contains(ne)) sendActivationEmail(ne))

You can make the pattern match work by having two different matches, but unless you're just calling the same method, there's some unwanted code duplication:
def f2(newEmail:Option[String], oldEmail:Option[String]) =
(newEmail, oldEmail) match {
case (Some(ne), None) =>
sendActivationEmail(ne)
case (Some(ne), Some(oe)) if ne != oe =>
sendActivationEmail(ne)
case _ =>
()
}

You can have multiple pattern matches as below,
test("given empty old email sends email to new email") {
val oldEmailOpt : Option[String] = None
val newEmailOpt : Option[String] = Some("this-is-my-beatifool email")
val result = newEmailOpt match {
case Some(newEmail) => oldEmailOpt match {
case Some(oldEmail) if !newEmail.equals(oldEmail) => "send email to new email"
case None => "send email to new email"
}
case None => "sending email to new email"
}
result shouldBe "send email to new email"
}

Related

Pattern match with variable parameters

Please suggest best was to implement the below code:
Requirement: pass single string or no parameters
object Twofer {
def twofer(name: String*): String = name match {
case Seq(nm) => s"One for $nm, one for me."
case List() => "One for you, one for me."
}
}
Maybe something similar to the following:
def twofer(names: String*): String = List(names: _*) match {
case Nil => "No names!"
case n :: Nil => s"Single name $n"
case ls => s"Multiple names $ls"
}

Scala match case on regex directly

I am trying to do something like the following:
list.foreach {x =>
x match {
case """TEST: .*""" => println( "TEST" )
case """OXF.*""" => println("XXX")
case _ => println("NO MATCHING")
}
}
The idea is to use it like groovy switch case regex match. But I can't seem to get to to compile. Whats the right way to do it in scala?
You could either match on a precompiled regular expression (as in the first case below), or add an if
clause. Note that you typically don't want to recompile the same regular expression on each case evaluation, but rather have it on an object.
val list = List("Not a match", "TEST: yes", "OXFORD")
val testRegex = """TEST: .*""".r
list.foreach { x =>
x match {
case testRegex() => println( "TEST" )
case s if s.matches("""OXF.*""") => println("XXX")
case _ => println("NO MATCHING")
}
}
See more information here and some background here.
Starting Scala 2.13, it's possible to directly pattern match a String by unapplying a string interpolator:
// val examples = List("Not a match", "TEST: yes", "OXFORD")
examples.map {
case s"TEST: $x" => x
case s"OXF$x" => x
case _ => ""
}
// List[String] = List("", "yes", "ORD")

Is there a way to see what a wildcard pattern is receiving during a match in Scala?

When doing pattern matching in an Akka or Scala Actor, is there a way to see what the match was NOT (i.e.) what is being evaluated by the wildcard _? Is there a simple way to see which message is being processed from the mailbox that it can't find a match for?
def receive = {
case A =>
case B =>
case C =>
...
case _ =>
println("what IS the message evaluated?")
}
Thanks,
Bruce
You can just define variable like this:
def receive = {
case A =>
case B =>
case C =>
...
case msg =>
println("unsupported message: " + msg)
}
You can even assign names to the messages that you are matching with #:
def receive = {
case msg # A => // do someting with `msg`
...
}
The "correct" way to do this in Akka is to override the "unhandled"-method, do what you want, and either delegate to the default behavior or replace it.
http://akka.io/api/akka/2.0-M4/#akka.actor.Actor
As for pattern matching in general, just match on anything, and bind it to a name, so you can refer to it:
x match {
case "foo" => whatever
case otherwise => //matches anything and binds it to the name "otherwise", use that inside the body of the match
}

How do I pattern match arrays in Scala?

My method definition looks as follows
def processLine(tokens: Array[String]) = tokens match { // ...
Suppose I wish to know whether the second string is blank
case "" == tokens(1) => println("empty")
Does not compile. How do I go about doing this?
If you want to pattern match on the array to determine whether the second element is the empty string, you can do the following:
def processLine(tokens: Array[String]) = tokens match {
case Array(_, "", _*) => "second is empty"
case _ => "default"
}
The _* binds to any number of elements including none. This is similar to the following match on Lists, which is probably better known:
def processLine(tokens: List[String]) = tokens match {
case _ :: "" :: _ => "second is empty"
case _ => "default"
}
What is extra cool is that you can use an alias for the stuff matched by _* with something like
val lines: List[String] = List("Alice Bob Carol", "Bob Carol", "Carol Diane Alice")
lines foreach { line =>
line split "\\s+" match {
case Array(userName, friends#_*) => { /* Process user and his friends */ }
}
}
Pattern matching may not be the right choice for your example. You can simply do:
if( tokens(1) == "" ) {
println("empty")
}
Pattern matching is more approriate for cases like:
for( t <- tokens ) t match {
case "" => println( "Empty" )
case s => println( "Value: " + s )
}
which print something for each token.
Edit: if you want to check if there exist any token which is an empty string, you can also try:
if( tokens.exists( _ == "" ) ) {
println("Found empty token")
}
case statement doesn't work like that. That should be:
case _ if "" == tokens(1) => println("empty")

How to match a string on a prefix and get the rest?

I can write the code like this:
str match {
case s if s.startsWith("!!!") => s.stripPrefix("!!!")
case _ =>
}
But I want to know is there any better solutions. For example:
str match {
case "!!!" + rest => rest
case _ =>
}
val r = """^!!!(.*)""".r
val r(suffix) = "!!!rest of string"
So suffix will be populated with rest of string, or a scala.MatchError gets thrown.
A different variant would be:
val r = """^(!!!){0,1}(.*)""".r
val r(prefix,suffix) = ...
And prefix will either match the !!! or be null. e.g.
(prefix, suffix) match {
case(null, s) => "No prefix"
case _ => "Prefix"
}
The above is a little more complex than you might need, but it's worth looking at the power of Scala's regexp integration.
Starting Scala 2.13, it's now possible to pattern match a String by unapplying a string interpolator:
"!!!hello" match {
case s"!!!$rest" => rest
case _ => "oups"
}
// "hello"
If it's the sort of thing you do often, it's probably worth creating an extractor
object BangBangBangString{
def unapply(str:String):Option[String]= {
str match {
case s if s.startsWith("!!!") => Some(s.stripPrefix("!!!"))
case _ => None
}
}
}
Then you can use the extractor as follows
str match{
case BangBangBangString(rest) => println(rest)
case _ => println("Doesn't start with !!!")
}
or even
for(BangBangBangString(rest)<-myStringList){
println("rest")
}
Good question !
Even i was trying a lot to find out the answer.
Here is a good link where I found the answer
object _04MatchExpression_PatternGuards {
def main(args: Array[String]): Unit = {
val url: String = "Jan";
val monthType = url match {
case url if url.endsWith(".org") => "Educational Websites";
case url if url.endsWith(".com") => "Commercial Websites";
case url if url.endsWith(".co.in") => "Indian Websites"
case _ => "Unknow Input";
}
}
}