I have a case class that has multiple parameters of which some are Options. Here is a simplified example:
case class Foobar(a: String, b: Option[String], c: Option[CustomClass])
I want to be able to match cases of Foobar where b and/or c is not None. For example, one case could be:
testResult match {
case Foobar("str1", Some(_), None) => "good"
case Foobar("str2", None, Some(_)) => "ok"
case _ => "bad"
}
Furthermore, I want to reference the case patterns via variables and this is where I'm stuck. I want to do something like the following:
val goodPat = Foobar("str1", Some(_), None) // compile fail
val okPat = Foobar("str2", None, Some(_)) // compile fail
testResult match {
case `goodPat` => "good"
case `okPat` => "ok"
case _ => "bad"
}
Is something like this possible? Is there another way to specify "not None"? Is there another way to approach this problem?
EDIT: I'm adding more details and context to the question. I have a large List of 2-tuples representing unit tests for a particular function. The 2-tuples represent the input and expected output. Eg.
// imagine this list is much bigger and Foobar contains more Option parameters
val tests = List(
("test1", Foobar("idkfa", None, None)),
// I know these fail to compile but I need to do something like this
("test2", Foobar("idclip", Some("baz"), Some(_)),
("test3", Foobar("iddqd", Some(_), None)
)
tests.foreach(test => {
val (input, expected) = test
myFunction(input) match {
case `expected` => println("ok")
case _ => println("bad")
}
})
I think you seeking for something like this:
case class DoomOpt(s: String)
case class Foobar(a: String, b: Option[String], c: Option[DoomOpt])
def myFunction(s: String): Foobar = { // your code here }
val tests = Map[String, PartialFunction[Foobar, Unit]](
"idkfa" → { case Foobar("idkfa", None, None) ⇒ },
"test2" → { case Foobar("idclip", Some("baz"), Some(_)) ⇒ },
"test3" → { case Foobar("iddqd", Some(_), None) ⇒ },
"idspispopd" → { case Foobar("idspispopd", Some(_), None) ⇒ }
)
tests.foreach { case (input, checker) =>
if (checker.isDefinedAt(myFunction(input)))
println("ok")
else
println("bad")
}
Pattern matching uses extractors which provide the unapply function to deconstruct the object. So... you can just supply your custom extractor in this case. Create a list of these extractor test cases and apply them one by one.
case class Foobar(s: String, o: Option[Int])
trait TestExtractor {
def unapply(fbar: Foobar): Boolean
}
object somePatExtractor extends TestExtractor {
def unapply(fbar: Foobar): Boolean = fbar match {
case Foobar("yes", Some(_)) => true
case _ => false
}
}
object nonePatExtractor extends TestExtractor {
def unapply(fbar: Foobar): Boolean = fbar match {
case Foobar("yes", None) => true
case _ => false
}
}
object bazPatExtractor extends TestExtractor {
def unapply(fbar: Foobar): Boolean = fbar match {
case Foobar("yes", Some("baz")) => true
case _ => false
}
}
val testList: List[(String, TestExtractor)] = List(("test1", nonePatExtractor), ("test2", bazPatExtractor), ("test3", somePatExtractor))
val fooToTest = Foobar("yes", Some(5))
testList.foreach({
case (testName, extractor) => {
fooToTest match {
case pat # extractor() => println("testName :: " + testName + ", Result :: ok")
case _ => println("testName :: " + testName + ", Result :: bad")
}
}
})
And if you are looking for a more extendible approach then you can consider something like following,
case class Foobar(s: String, o1: Option[Int], o2: Option[String])
case class TestCondition(df: Foobar => Boolean) {
def test(foobar: Foobar): Boolean = df(foobar)
}
val o1IsNone = TestCondition(f => f.o1.isEmpty)
val o1IsSome = TestCondition(f => f.o1.isDefined)
val o2IsNone = TestCondition(f => f.o2.isEmpty)
val o2IsSome = TestCondition(f => f.o2.isDefined)
case class TestCase(tcs: List[TestCondition]) {
def test(foobar: Foobar) = tcs.foldLeft(true)({ case (acc, tc) => acc && tc.test(foobar) })
}
val testList = List[(String, TestCase)](
("test1", TestCase(List(o1IsSome, o2IsSome))),
("test2", TestCase(List(o1IsSome, o2IsNone))),
("test3", TestCase(List(o1IsNone, o2IsSome))),
("test4", TestCase(List(o1IsNone, o2IsNone)))
)
val foobarToTest = Foobar("yes", Some(5), None)
testList.foreach({
case (testName, testCase) => {
foobarToTest match {
case foobar: Foobar if testCase.test(foobar) => println("testName :: " + testName + ", Result :: ok")
case _ => println("testName :: " + testName + ", Result :: bad")
}
}
})
Related
Using Scala I am trying to pattern match against a class that returns a value type of Any to pull out any List[Any] and pattern match against List[Long] and List[Double].
Is there a more elegant way to do this?
Running scala 2.11
case class Accumulator (
name: Option[String],
value: Option[Any]
)
def bar[T <: Any](value: T): Unit = {
val listOfAny = value.asInstanceOf[List[Any]]
val listOfTypes = listOfAny.map(x => x.getClass.getSimpleName).toSet
listOfTypes.size match {
case 1 => listOfTypes.head match {
case "Long" => println("Long list")
case "Double" => println("Double list")
case _ => Unit
}
case _ => Unit //Probably throw an error log
}
}
def foo(accumulator: Accumulator): Unit = {
accumulator match {
case Accumulator(_, Some(value)) => value match {
case v if v.isInstanceOf[List[_]] => bar(v)
case _ => Unit
}
case _ => Unit
}
}
//Should print out "Long List"
foo(Accumulator(None, Some(List(1L, 2L, 3L))))
//Should print out "Double List"
foo(Accumulator(None, Some(List(1.0, 2.0, 3.0))))
Edit:
Was able to clean up the string matching with stable identifiers
case class Accumulator (
name: Option[String],
value: Option[Any]
)
def bar[T <: Any](value: T): Unit = {
val listOfAny = value.asInstanceOf[List[Any]]
val listOfTypes = listOfAny.map(x => x.getClass).toSet
listOfTypes.size match {
case 1 =>
val headType: Class[_] = listOfTypes.head
// Stable identifiers
val ClassOfLong: Class[java.lang.Long] = classOf[java.lang.Long]
val ClassOfDouble: Class[java.lang.Double] = classOf[java.lang.Double]
headType match {
case ClassOfLong =>
val result: Long = listOfAny.asInstanceOf[List[Long]].sum
println(s"Long List sum: $result")
case ClassOfDouble =>
val result: Double = listOfAny.asInstanceOf[List[Double]].sum
println(s"Double List sum: $result")
case _ => Unit
}
case _ => Unit //Probably throw an error log
}
}
def foo(accumulator: Accumulator): Unit = {
accumulator match {
case Accumulator(_, Some(value)) => value match {
case v if v.isInstanceOf[List[_]] => bar(v)
case _ => Unit
}
case _ => Unit
}
}
//Should print out "Long List sum: 6"
foo(Accumulator(None, Some(List(1L, 2L, 3L))))
//Should print out "Double List sum: 6.0"
foo(Accumulator(None, Some(List(1.0, 2.0, 3.0))))
If the lists are not empty and all the elements are of the same type, you can match both the returned object and the first element in the list. Something like this
def surprise(): Any = Random.nextInt(3) match {
case 0 => List(1L, 2L, 3L)
case 1 => List(0.5, 1.5, 2.5)
case _ => "foo"
}
0 to 10 foreach { _ =>
surprise() match {
case l # List(_: Long, _*) =>
println(s"Longs: $l")
case l # List(_: Double, _*) =>
println(s"Doubles: $l")
case x =>
println(s"Something else: $x")
}
}
Output:
"""
Something else: foo
Something else: foo
Longs: List(1, 2, 3)
Doubles: List(0.5, 1.5, 2.5)
Doubles: List(0.5, 1.5, 2.5)
Doubles: List(0.5, 1.5, 2.5)
Doubles: List(0.5, 1.5, 2.5)
Doubles: List(0.5, 1.5, 2.5)
Something else: foo
Longs: List(1, 2, 3)
Something else: foo
"""
case class Accumulator (
name: Option[String],
value: Option[Any]
)
def bar[T <: Any](value: T): Unit = {
val listOfAny = value.asInstanceOf[List[Any]]
val listOfTypes = listOfAny.map(x => x.getClass).toSet
listOfTypes.size match {
case 1 =>
val headType: Class[_] = listOfTypes.head
// Stable identifiers
val ClassOfLong: Class[java.lang.Long] = classOf[java.lang.Long]
val ClassOfDouble: Class[java.lang.Double] = classOf[java.lang.Double]
headType match {
case ClassOfLong =>
val result: Long = listOfAny.asInstanceOf[List[Long]].sum
println(s"Long List sum: $result")
case ClassOfDouble =>
val result: Double = listOfAny.asInstanceOf[List[Double]].sum
println(s"Double List sum: $result")
case _ => Unit
}
case _ => Unit //Probably throw an error log
}
}
def foo(accumulator: Accumulator): Unit = {
accumulator match {
case Accumulator(_, Some(value)) => value match {
case v if v.isInstanceOf[List[_]] => bar(v)
case _ => Unit
}
case _ => Unit
}
}
//Should print out "Long List sum: 6"
foo(Accumulator(None, Some(List(1L, 2L, 3L))))
//Should print out "Double List sum: 6.0"
foo(Accumulator(None, Some(List(1.0, 2.0, 3.0))))
I currently have something that looks like:
Class UserLocation {
def handleUser1(user: User): Unit = user.location match {
case ... => ..
case ... => ..
case _ => ..
}
}
Class UserLocation2 {
def handleUser2(user: User): Unit = user.location match {
case ... => ..
case ... => ..
case _ => ..
}
}
Class UserLocation3 {
def handleUser3(user: User): Unit = user.location match {
case ... => ..
case ... => ..
case _ => ..
}
}
How can I convert the above to a partial function so I can do something like:
def handleUser(user: User): Unit = handleUser1(user) orElse handleUser2(user) orElse handleUser3(user)
There are a few problems with the your layout and your goal. For one thing, all the handleUser methods are hidden in their own class so the goal, as stated, is impossible because they aren't all in scope at the same time. They might be pulled into scope by making the classes implicit, but for that each class needs a constructor parameter.
So here's one possible solution achieved by dropping all the individual classes.
case class User(location: String)
val handleUser1: PartialFunction[User,Unit] = {
case User("Bern") => println("there")
case User("NYC") => println("here")
}
val handleUser2: PartialFunction[User,Unit] = {
case User("Spain") => println("there")
case User("USA") => println("here")
}
val handleUser3: PartialFunction[User,Unit] = {
case User("moon") => println("far")
case User("earth") => println("near")
case User(_) => println("unknown") // the only default
}
val handleUser = handleUser1 orElse handleUser2 orElse handleUser3
handleUser(User("Bern")) // "there"
handleUser(User("moon")) // "far"
handleUser(User("Boon")) // "unknown"
You can define the functions as PartialFunctions and then compose them using orElse:
val f :PartialFunction[Int, Int] = {
case 1 => 1
}
val f2 :PartialFunction[Int, Int] = {
case 2 => 2
}
val f3 :PartialFunction[Int, Int] = {
case 3 => 3
}
def g = f orElse f2 orElse f3
g(1) // 1
g(2) // 2
g(3) // 3
g(4) // scala.MatchError
P.S
#Suma - Thanks for the input.
There is a list with type List[Option[String]], it may contain Some or None
val list:List[Option[String]] = List(Some("aaa"), None, Some("bbb"))
list match {
case /*List with all Some*/ => println("all items are Some")
case /*List with all None*/ => println("all items are None")
case /*List with Some and None*/ => println("Contain both Some and None")
}
But I don't know how to write it. Is it possible to use pattern matching?
You can write custom extractors:
object AllSome {
def unapply[T](l: List[Option[T]]) = l.forall(_.isDefined)
}
object AllNone {
def unapply[T](l: List[Option[T]]) = l.forall(_ == None)
}
object Mixed {
def unapply[T](l: List[Option[T]]) = !AllNone.unapply(l) && !AllSome.unapply(l)
}
And use them like:
list match {
case AllSome() => ???
case AllNone() => ???
case Mixed() => ???
}
One approach involves flattening the list and comparing the resulting length with the original length, like this,
list.flatten.length match {
case 0 => println("All items are None")
case len if len < l.length => println("Contain both Some and None")
case _ => println("All items are Some")
}
Update
To get the contents of each Some, simply list.flatten , namely for instance,
list.flatten
res: List(aaa, bbb)
and
List().flatten
res: List[Nothing] = List()
If you really want a solution with well-defined semantics and based on pattern matching you could do something as follows:
abstract class CollectionStatus
case object Empty extends CollectionStatus
case object AllSome extends CollectionStatus
case object AllNone extends CollectionStatus
case object Mixed extends CollectionStatus
object CollectionStatus {
def default: CollectionStatus = Empty
}
def folder(status: CollectionStatus, o: Option[_]): CollectionStatus = {
(status, o) match {
case (Empty, Some(_)) => AllSome
case (Empty, None) => AllNone
case (AllSome, Some(_)) => AllSome
case (AllNone, None) => AllNone
case _ => Mixed
}
}
Here's how I would use it:
List[Option[String]]().foldLeft(CollectionStatus.default)(folder _) //Empty
List(Option("foo"), Option("bar")).foldLeft(CollectionStatus.default)(folder _) //AllSome
List(Option("foo"), None).foldLeft(CollectionStatus.default)(folder _) //Mixed
List(None, None).foldLeft(CollectionStatus.default)(folder _) //AllNone
This could be further improved by replacing foldLeft with a tail recursive function that would accumulate the status of the list, and finish its computations without traversing the whole list if it recognized the list was "Mixed" already:
import scala.annotation.tailrec
def listStatus(list: List[Option[_]]): CollectionStatus = {
#tailrec
def inner(acc: CollectionStatus, ls: List[Option[_]]): CollectionStatus = {
acc match {
case Mixed => Mixed
case s => {
ls match {
case Nil => s
case h :: t => {
inner(folder(s, h), t)
}
}
}
}
}
inner(CollectionStatus.default, list)
}
val l: List[Option[String]] = List(Some("aaa"), None, Some("bbb"))
l.groupBy({
case Some(_) => "s"
case None => "n"
}).toList match {
case List(_,_) => println("both")
case List(a) => if( a._1 == "n") println("none") else println("some")
}
match (str) {
case "String1" => ???
case "String2" => ???
}
This is case sensitive matching. How to write case insensitive matching? I know I can call toLowerCase for each branch, but I want more elegant solution.
Basic approach:
You could use Pattern Guards and Regular Expressions
str match {
case s if s matches "(?i)String1" => 1
case s if s matches "(?i)String2" => 2
case _ => 0
}
Sophisticated method:
Implicits with String Interpolation and Regex
implicit class CaseInsensitiveRegex(sc: StringContext) {
def ci = ( "(?i)" + sc.parts.mkString ).r
}
def doStringMatch(str: String) = str match {
case ci"String1" => 1
case ci"String2" => 2
case _ => 0
}
Some example usage in the REPL:
scala> doStringMatch("StRINg1")
res5: Int = 1
scala> doStringMatch("sTring2")
res8: Int = 2
Easy solution:
val str = "string1"
str toUpperCase match (str) {
case "STRING1" => ???
case "STRING2" => ???
}
Another approach that does not depend on regexes or interpolaters:
implicit class StringExtensions(val s: String) extends AnyVal {
def insensitive = new {
def unapply(other: String) = s.equalsIgnoreCase(other)
}
}
val test1 = "Bye".insensitive
val test2 = "HELLo".insensitive
"Hello" match {
case test1() => println("bad!")
case test2() => println("sweet!")
case _ => println("fail!")
}
Here is another way using interpolaters but no regex:
implicit class StringInterpolations(sc: StringContext) {
def ci = new {
def unapply(other: String) = sc.parts.mkString.equalsIgnoreCase(other)
}
}
"Hello" match {
case ci"Bye" => println("bad!")
case ci"HELLO" => println("sweet!")
case _ => println("fail!")
}
The above can also be used to pattern match inside case classes e.g.:
case class Dog(name: String)
val fido = Dog("FIDO")
fido match {
case Dog(ci"fido") => "woof"
case _ => "meow :("
}
I have multiple Option's. I want to check if they hold a value. If an Option is None, I want to reply to user about this. Else proceed.
This is what I have done:
val name:Option[String]
val email:Option[String]
val pass:Option[String]
val i = List(name,email,pass).find(x => x match{
case None => true
case _ => false
})
i match{
case Some(x) => Ok("Bad Request")
case None => {
//move forward
}
}
Above I can replace find with contains, but this is a very dirty way. How can I make it elegant and monadic?
Edit: I would also like to know what element was None.
Another way is as a for-comprehension:
val outcome = for {
nm <- name
em <- email
pwd <- pass
result = doSomething(nm, em, pwd) // where def doSomething(name: String, email: String, password: String): ResultType = ???
} yield (result)
This will generate outcome as a Some(result), which you can interrogate in various ways (all the methods available to the collections classes: map, filter, foreach, etc.). Eg:
outcome.map(Ok(result)).orElse(Ok("Bad Request"))
val ok = Seq(name, email, pass).forall(_.isDefined)
If you want to reuse the code, you can do
def allFieldValueProvided(fields: Option[_]*): Boolean = fields.forall(_.isDefined)
If you want to know all the missing values then you can find all missing values and if there is none, then you are good to go.
def findMissingValues(v: (String, Option[_])*) = v.collect {
case (name, None) => name
}
val missingValues = findMissingValues(("name1", option1), ("name2", option2), ...)
if(missingValues.isEmpty) {
Ok(...)
} else {
BadRequest("Missing values for " + missingValues.mkString(", ")))
}
val response = for {
n <- name
e <- email
p <- pass
} yield {
/* do something with n, e, p */
}
response getOrElse { /* bad request /* }
Or, with Scalaz:
val response = (name |#| email |#| pass) { (n, e, p) =>
/* do something with n, e, p */
}
response getOrElse { /* bad request /* }
if ((name :: email :: pass :: Nil) forall(!_.isEmpty)) {
} else {
// bad request
}
I think the most straightforward way would be this:
(name,email,pass) match {
case ((Some(name), Some(email), Some(pass)) => // proceed
case _ => // Bad request
}
A version with stone knives and bear skins:
import util._
object Test extends App {
val zero: Either[List[Int], Tuple3[String,String,String]] = Right((null,null,null))
def verify(fields: List[Option[String]]) = {
(zero /: fields.zipWithIndex) { (acc, v) => v match {
case (Some(s), i) => acc match {
case Left(_) => acc
case Right(t) =>
val u = i match {
case 0 => t copy (_1 = s)
case 1 => t copy (_2 = s)
case 2 => t copy (_3 = s)
}
Right(u)
}
case (None, i) =>
val fails = acc match {
case Left(f) => f
case Right(_) => Nil
}
Left(i :: fails)
}
}
}
def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)
val name:Option[String] = Some("Bob")
val email:Option[String]= None
val pass:Option[String] = Some("boB")
val res = verify(List(name,email,pass))
res.fold(fail, (consume _).tupled)
val res2 = verify(List(name, Some("bob#bob.org"),pass))
res2.fold(fail, (consume _).tupled)
}
The same thing, using reflection to generalize the tuple copy.
The downside is that you must tell it what tuple to expect back. In this form, reflection is like one of those Stone Age advances that were so magical they trended on twitter for ten thousand years.
def verify[A <: Product](fields: List[Option[String]]) = {
import scala.reflect.runtime._
import universe._
val MaxTupleArity = 22
def tuple = {
require (fields.length <= MaxTupleArity)
val n = fields.length
val tupleN = typeOf[Tuple2[_,_]].typeSymbol.owner.typeSignature member TypeName(s"Tuple$n")
val init = tupleN.typeSignature member nme.CONSTRUCTOR
val ctor = currentMirror reflectClass tupleN.asClass reflectConstructor init.asMethod
val vs = Seq.fill(n)(null.asInstanceOf[String])
ctor(vs: _*).asInstanceOf[Product]
}
def zero: Either[List[Int], Product] = Right(tuple)
def nextProduct(p: Product, i: Int, s: String) = {
val im = currentMirror reflect p
val ts = im.symbol.typeSignature
val copy = (ts member TermName("copy")).asMethod
val args = copy.paramss.flatten map { x =>
val name = TermName(s"_$i")
if (x.name == name) s
else (im reflectMethod (ts member x.name).asMethod)()
}
(im reflectMethod copy)(args: _*).asInstanceOf[Product]
}
(zero /: fields.zipWithIndex) { (acc, v) => v match {
case (Some(s), i) => acc match {
case Left(_) => acc
case Right(t) => Right(nextProduct(t, i + 1, s))
}
case (None, i) =>
val fails = acc match {
case Left(f) => f
case Right(_) => Nil
}
Left(i :: fails)
}
}.asInstanceOf[Either[List[Int], A]]
}
def consume(name: String, email: String, pass: String) = Console println s"$name/$email/$pass"
def fail(is: List[Int]) = is map List("name","email","pass") foreach (Console println "Missing: " + _)
val name:Option[String] = Some("Bob")
val email:Option[String]= None
val pass:Option[String] = Some("boB")
type T3 = Tuple3[String,String,String]
val res = verify[T3](List(name,email,pass))
res.fold(fail, (consume _).tupled)
val res2 = verify[T3](List(name, Some("bob#bob.org"),pass))
res2.fold(fail, (consume _).tupled)
I know this doesn't scale well, but would this suffice?
(name, email, pass) match {
case (None, _, _) => "name"
case (_, None, _) => "email"
case (_, _, None) => "pass"
case _ => "Nothing to see here"
}