I want to do pattern matching in Scala but it should be case insensitive. Is there a way I can write the code without using separate 'case' clauses for lower and upper cases
//person class with first name and last name
case class Person (var fn: String, val ln: String) {
val name = fn
val lastName = ln
}
//two instances. Same last name but cases are different
val a2 = Person("Andy","Cale")
val a3 = Person("Andy","cale")
def isCale(person:Person) {
person match {
//I want that this case should be case insensitive
case Person(_,"Cale") => println("last-name Cale")
case _ => println("not Cale")
}
}
isCale(a2)
lastname Cale
//I want this to also match
isCale(a3)
not Cale
One alternative is to extract the last name and compare as follows but I am interested in finding if there is a way to do this in case itself.
def isCale(a2:A2) {
val s = a2.ln
s.toLowerCase match {
case "cale" => println("last-name Cale")
case _ => println("not Cale")
}
You can use a guard:
def main(args: Array[String]): Unit = {
case class Person(firstName: String, lastName: String)
val p = Person("Yuval", "Itzchakov")
p match {
case Person(_, lastName) if lastName.equalsIgnoreCase("itzchakov") =>
println(s"Last name is: $lastName")
case _ => println("Not itzchakov")
}
}
Side note - case class parameters will be attached as vals on the declared class, there's no need for the additional assignment and no need for the val/var definition on the constructor.
You can use an extractor:
scala> val r = "(?i:it.*ov)".r
r: scala.util.matching.Regex = (?i:it.*ov)
scala> case class Person(firstName: String, lastName: String)
defined class Person
scala> val ps = Person("Fred", "Itchikov") :: Person("Yuval", "Itzchakov") :: Nil
ps: List[Person] = List(Person(Fred,Itchikov), Person(Yuval,Itzchakov))
scala> ps collect { case Person(_, n # r()) => n }
res0: List[String] = List(Itchikov, Itzchakov)
Related
Let's say i have 2 cases classes:
case class Money(amount: Int, currency: String)
case class Human(name: String, money: Money)
is there a nice way to "translate" a list of strings to class Human? smth like:
def superMethod[A](params: List[String]): A = ???
val params: List[Any] = List("john", 100, "dollar")
superMethod(params) // => Human("john", Money(100, "dollar"))
so essentially i know type A only in runtime
UPDATE: i found ~ what i was looking for. it seems i can do it via shapeless. example i found in github
Here is an implementation that works for generic classes A.
It relies on runtime reflection (that is, a different TypeTag can be passed to the method at runtime). The following obvious conditions must be fulfilled in order to use this method:
A must be on the class path, or otherwise be loadable by the used class loader
TypeTag must be available for A at the call site.
The actual implementation is in the Deserializer object. Then comes a little demo.
The deserializer:
import scala.reflect.runtime.universe.{TypeTag, Type}
object Deserializer {
/** Extracts an instance of type `A` from the
* flattened `Any` constructor arguments, and returns
* the constructed instance together with the remaining
* unused arguments.
*/
private def deserializeRecHelper(
flattened: List[Any],
tpe: Type
): (Any, List[Any]) = {
import scala.reflect.runtime.{universe => ru}
// println("Trying to deserialize " + tpe + " from " + flattened)
// println("Constructor alternatives: ")
// val constructorAlternatives = tpe.
// member(ru.termNames.CONSTRUCTOR).
// asTerm.
// alternatives.foreach(println)
val consSymb = tpe.
member(ru.termNames.CONSTRUCTOR).
asTerm.
alternatives(0).
asMethod
val argsTypes: List[Type] = consSymb.paramLists(0).map(_.typeSignature)
if (tpe =:= ru.typeOf[String] || argsTypes.isEmpty) {
val h :: t = flattened
(h, t)
} else {
val args_rems: List[(Any, List[Any])] = argsTypes.scanLeft(
(("throwaway-sentinel-in-deserializeRecHelper": Any), flattened)
) {
case ((_, remFs), t) =>
deserializeRecHelper(remFs, t)
}.tail
val remaining: List[Any] = args_rems.last._2
val args: List[Any] = args_rems.unzip._1
val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)
val classMirror = runtimeMirror.reflectClass(tpe.typeSymbol.asClass)
val cons = classMirror.reflectConstructor(consSymb)
// println("Build constructor arguments array for " + tpe + " : " + args)
val obj = cons.apply(args:_*)
(obj, remaining)
}
}
def deserialize[A: TypeTag](flattened: List[Any]): A = {
val (a, rem) = deserializeRecHelper(
flattened,
(implicitly: TypeTag[A]).tpe
)
require(
rem.isEmpty,
"Superfluous arguments remained after deserialization: " + rem
)
a.asInstanceOf[A]
}
}
Demo:
case class Person(id: String, money: Money, pet: Pet, lifeMotto: String)
case class Money(num: Int, currency: String)
case class Pet(color: String, species: Species)
case class Species(description: String, name: String)
object Example {
def main(args: Array[String]): Unit = {
val data = List("Bob", 42, "USD", "pink", "invisible", "unicorn", "what's going on ey?")
val p = Deserializer.deserialize[Person](data)
println(p)
}
}
Output:
Person(Bob,Money(42,USD),Pet(pink,Species(invisible,unicorn)),what's going on ey?)
Discussion
This implementation is not restricted to case classes, but it requires each "Tree-node-like" class to have exactly one constructor that accepts either
primitive types (Int, Float), or
strings, or
other "Tree-node-like" classes.
Note that the task is somewhat ill-posed: what does it mean to say that all constructor arguments are flattened in a single list? Given the class Person(name: String, age: Int), will the List[Any] contain every single byte of the name as a separate entry? Probably not. Therefore, strings are handled by the deserializer in a special way, and all other collection-like entities are not supported for the same reasons (unclear where to stop parsing, because size of the collection is not known).
In case A is not a generic type, but effectively Human, you can use a companion object to the case class Human:
object Human {
def fromList(list: List[String]): Human = list match {
case List(name, amount, currency) => Human(name, Money(amount.toInt, currency))
case _ => handle corner case
}
}
Which you can call:
Human.fromList(List("john", "100", "dollar"))
To make it safe, don't forget to handle the case of lists whose size wouldn't be 3; and of lists whose 2nd element can't be cast to an Int:
import scala.util.Try
object Human {
def fromList(list: List[String]): Option[Human] = list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount.toInt, currency))).toOption
case _ => None
}
}
Edit: Based on your last comment, you might find this usefull:
case class Money(amount: Int, currency: String)
case class Human(name: String, money: Money)
case class SomethingElse(whatever: Double)
object Mapper {
def superMethod(list: List[String]): Option[Any] =
list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount.toInt, currency))).toOption
case List(whatever) => Try(SomethingElse(whatever.toDouble)).toOption
case _ => None
}
}
println(Mapper.superMethod(List("john", 100, "dollar")))
> Some(Human(john,Money(100,dollar)))
println(Mapper.superMethod(List(17d)))
> Some(SomethingElse(17.0))
or alternatively:
object Mapper {
def superMethod[A](list: List[String]): Option[A] =
(list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount, currency))).toOption
case List(whatever) =>
Try(SomethingElse(whatever.toDouble)).toOption
case _ => None
}).map(_.asInstanceOf[A])
}
println(Mapper.superMethod[Human](List("john", "100", "dollar")))
> Some(Human(john,Money(100,dollar)))
println(Mapper.superMethod[SomethingElse](List("17.2")))
> Some(SomethingElse(17.0))
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")
}
}
})
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 some code
case class A(s:String) {val x = "hello"}
Why can't I access the static variable x without instantiating the class A? If I type
A.x
I get the following error:
error: value x is not a member of object A
Edit:
I missed out mentioning the remaining code. Here is the example that I would like to use:
abstract class A { val name:String }
case class B(i:Int) extends A { val name = "B" }
case class C(b:Boolean) extends A { val name = "C" }
def getType(s:String) = s match {
case B.name => println ("Object B")
case C.name => println ("Object C")
}
The error:
scala> def getType(s:String) = s match {
| case B.name => println ("Object B")
| case C.name => println ("Object C")
| }
<console>:11: error: value name is not a member of object B
case B.name => println ("Object B")
^
<console>:12: error: value name is not a member of object C
case C.name => println ("Object C")
^
As to why use case classes, the case classes are not defined for this purpose. Elsewhere I have some code like:
def func(a:A) = a match {
case b:B =>
case c:C =>
...
}
Well, you cannot call the "static" variable x, because in Scala there are no static variables. You are declaring x to be a regular member of class A, which you could access if you had an instance of class A.
What you try to do by calling A.x is accessing a value with the name "A". There happens to be such a value in scope - the compiler generated companion object for your case class A.
But this object A has no member "x", therefore the compiler rightly complains about it.
You can add the value x to the object A instead of the class/type A by doing the following:
case class A(s:String)
object A { val x = "hello" }
From the small amount you described of the problem, it sounds like case classes are just not for you.
Alternate patterns include...
Constants:
val Abs1 = "1" //note that it starts with an uppercase letter
val s: String = ...
s match {
case Abs1 => ...
case _ =>
}
Extractors:
object Positive {
def unapply(i: Int): Option[Int] = if(i >= 0) Some(i) else None
}
val i: Int = ...
i match {
case Positive(p) => ... //p will be bound to the matched positive number
case _ => ...
}
Case Classes (used properly):
case class MyType(s: String)
val x: MyType = ...
x match {
case MyType("a") => ...
case MyType("b") => ...
case MyType(matched) => ...
//matched will be bound to whatever string was used to construct the MyType instance
}
Case Objects:
abstract sealed trait Foo { def s: String }
case object Bar extends Foo { val s = "I'm a Bar" }
case object Baz extends Foo { val s = "I'm a Baz" }
val x: Foo = ...
x match {
case Bar => ...
case Baz => ...
//no other possibilities because Foo is sealed
}
Leaving aside issues of design for a moment.
If you need a workaround then you can bind an identifier to a matched case class or case object and use the bound identifier to access members of the matched entity. The bound identifier can be used in guard statements in the match or in the action provided for the match:
case class Bob(x: String, y: String) {val z = "Bragging"}
val bob1 = Bob("Big", "Bad")
bob1 match {
case b # Bob(x, y) if b.z == "Bragging" => println("Got "+x+", "+y+", "+b.z+" Bob!")
case _ =>
}
case object Bob {val z = "Balding"}
val bob2 = Bob
bob2 match {
case b # Bob if b.z == "Balding" => println("Got "+b.z+" Bob!")
case _ =>
}
Returning to design, in your case class definition you declare 'name' in the constructor body of B but you would get more useability from having 'name' as a parameter:
case class B(i: Int, name: String) extends A
Which could then match like this:
def getType(s:String) = s match {
case B(_, name) => println ("Object B("+name+")")
...
Finally it's hard to say without further detail but I suspect that mapping case classes to a large set of similar entities on a one to one basis is perhaps not the best choice, better to use case objects, or instances of a limited number of case classes or even tuples.
I'm doing matching against some case classes and would like to handle two of the cases in the same way. Something like this:
abstract class Foo
case class A extends Foo
case class B(s:String) extends Foo
case class C(s:String) extends Foo
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(sb) | C(sc) => "B"
case _ => "default"
}
}
But when I do this I get the error:
(fragment of test.scala):10: error: illegal variable in pattern alternative
case B(sb) | C(sc) => "B"
I can get it working of I remove the parameters from the definition of B and C but how can I match with the params?
Looks like you don't care about the values of the String parameters, and want to treat B and C the same, so:
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(_) | C(_) => "B"
case _ => "default"
}
}
If you must, must, must extract the parameter and treat them in the same code block, you could:
def matcher(l: Foo): String = {
l match {
case A() => "A"
case bOrC # (B(_) | C(_)) => {
val s = bOrC.asInstanceOf[{def s: String}].s // ugly, ugly
"B(" + s + ")"
}
case _ => "default"
}
}
Though I feel it would be much cleaner to factor that out into a method:
def doB(s: String) = { "B(" + s + ")" }
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(s) => doB(s)
case C(s) => doB(s)
case _ => "default"
}
}
There are a couple of ways that I can see to achieve what you are after, if you have some commonality between case classes. The first is to have the case classes extend a trait which declares the commonality, the second is to use a structural type which removes the need to extend your case classes.
object MuliCase {
abstract class Foo
case object A extends Foo
trait SupportsS {val s: String}
type Stype = Foo {val s: String}
case class B(s:String) extends Foo
case class C(s:String) extends Foo
case class D(s:String) extends Foo with SupportsS
case class E(s:String) extends Foo with SupportsS
def matcher1(l: Foo): String = {
l match {
case A => "A"
case s: Stype => println(s.s); "B"
case _ => "default"
}
}
def matcher2(l: Foo): String = {
l match {
case A => "A"
case s: SupportsS => println(s.s); "B"
case _ => "default"
}
}
def main(args: Array[String]) {
val a = A
val b = B("B's s value")
val c = C("C's s value")
println(matcher1(a))
println(matcher1(b))
println(matcher1(c))
val d = D("D's s value")
val e = E("E's s value")
println(matcher2(d))
println(matcher2(e))
}
}
The structural type method generates a warning about erasure which, at present I'm not sure how to eliminate.
Well, it doesn't really make sense, does it? B and C are mutually exclusive, so either sb or sc get bound, but you don't know which, so you'd need further selection logic to decide which to use (given that they were bound to a Option[String], not a String). So there's nothing gained over this:
l match {
case A() => "A"
case B(sb) => "B(" + sb + ")"
case C(sc) => "C(" + sc + ")"
case _ => "default"
}
Or this:
l match {
case A() => "A"
case _: B => "B"
case _: C => "C"
case _ => "default"
}