Case insensitive pattern matching for strings - scala

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 :("
}

Related

Convert match expressions so I can combine them using combinators like orElse

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.

case insensitive pattern match in Scala

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)

Case Classes w/ Option Parameters & Case Matching

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")
}
}
})

Can I use a class variable in a Scala match statement?

Say I have something like this:
obj match {
case objTypeOne : TypeOne => Some(objTypeOne)
case objTypeTwo : TypeTwo => Some(objTypeTwo)
case _ => None
}
Now I want to generalise, to pass in one of the types to match:
obj match {
case objTypeOne : clazz => Some(objTypeOne)
case objTypeTwo : TypeTwo => Some(objTypeTwo)
case _ => None
}
But this isn't allowed, I think for syntactic rather than semantic reasons (although I guess also that even though the clazz is a Class[C] the type is erased and so the type of the Option will be lost).
I ended up with:
if(clazzOne.isAssignableFrom(obj.getClass)) Some(clazz.cast(obj))
if(obj.isInstanceOf[TypeTwo]) Some(obj.asInstanceOf[TypeTwo])
None
I just wondered if there was a nicer way.
You could define an extractor to match your object:
class IsClass[T: Manifest] {
def unapply(any: Any): Option[T] = {
if (implicitly[Manifest[T]].erasure.isInstance(any)) {
Some(any.asInstanceOf[T])
} else {
None
}
}
}
So let's test it:
class Base { def baseMethod = () }
class Derived extends Base
val IsBase = new IsClass[Base]
def test(a:Any) = a match {
case IsBase(b) =>
println("base")
b.baseMethod
case _ => println("?")
}
test(new Base)
test(1)
You will have to define a val for your extractor, you can't inline IsBase, for example. Otherwise it would be interpreted as an extractor.
You could use pattern guards to achieve that. Try something like this:
obj match {
case objTypeTwo : TypeTwo => Some(objTypeTwo)
case objTypeOne if clazz.isAssignableFrom(objTypeOne.getClass) => Some(clazz.cast(objTypeOne))
case _ => None
}
You can use a local type alias for that:
def matcher[T](obj: Any)(implicit man: Manifest[T]) = {
val instance = man.erasure.newInstance.asInstanceOf[AnyRef]
type T = instance.type // type alias
obj match {
case objTypeOne : T => "a"
case objTypeTwo : TypeTwo => "b"
case _ => "c"
}
}
scala> matcher[TypeOne](TypeOne())
res108: java.lang.String = a
scala> matcher[TypeTwo](TypeOne())
res109: java.lang.String = c
UPDATE: Aaron Novstrup has pointed out that singleton type will only work if man.erasure.newInstance==obj (see §3.2.1 of the spec)

Match multiple cases classes in scala

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"
}