Pattern Matching to check if string is null or empty - scala

Is it possible to check if string is null or empty using match?
I'm trying to do something like:
def sendToYahoo(message:Email) ={
val clientConfiguration = new ClientService().getClientConfiguration()
val messageId : Seq[Char] = message.identifier
messageId match {
case messageId.isEmpty => validate()
case !messageId.isEmpty => //blabla
}
}
But i have a compile error.
Thank in advance.

You can write a simple function like:
def isEmpty(x: String) = Option(x).forall(_.isEmpty)
or
def isEmpty(x: String) = x == null || x.isEmpty
You might also want to trim the string, if you consider " " to be empty as well.
def isEmpty(x: String) = x == null || x.trim.isEmpty
and then use it
val messageId = message.identifier
messageId match {
case id if isEmpty(id) => validate()
case id => // blabla
}
or without a match
if (isEmpty(messageId)) {
validate()
} else {
// blabla
}
or even
object EmptyString {
def unapply(s: String): Option[String] =
if (s == null || s.trim.isEmpty) Some(s) else None
}
message.identifier match {
case EmptyString(s) => validate()
case _ => // blabla
}

def isNullOrEmpty[T](s: Seq[T]) = s match {
case null => true
case Seq() => true
case _ => false
}

If you're looking to check whether a String is null or empty, here's a one-liner that does that. You wrap it with an Option, use filter to make sure it's not an empty String then call getOrElse to provide a default value in case the original value is either null or an empty String.
Option(s).filter(!_.isEmpty).getOrElse(columnName)

Related

Why is it the empty element of a List are not replaced with default value in Scala?

I have the below Scala Code
Code:
object ReplaceNulls {
def main(args:Array[String]) = {
val myList = List("surender", "", null)
val myUpdatedList = myList.map {
case a: String => a
case null => "OTHERS"
case "" => "OTHERS"
}
println(myUpdatedList)
}
This above Code gives me the below Output
List(surender, , OTHERS)
But the expected output is below
List(surender,OTHERS,OTHERS)
What went wrong in my code ?
Because "" is also of type string and will match the first case i.e case a:String. You can try changing the order of case statements
object ReplaceNulls {
def main(args:Array[String])={
val myList = List("surender","",null)
val myUpdatedList = myList.map { x => x match{
case "" =>"OTHERS"
case a:String => a
case null => "OTHERS"
}
}
println(myUpdatedList)
}
}

Is it possible to pattern match on type String and get the result trimmed?

Say I have code like this:
def isCryptoExample(parts: Seq[Any]): Boolean = parts match {
case List(loneWord) => false
case List(subject: String, verb: String) =>
((subject.trim startsWith "Alice") || (subject.trim startsWith "Bob")) &&
((verb.trim == "encrypts") || (verb.trim == "decrypts"))
}
Those repeated .trim calls are ugly, but adding statements like val subjectTrimmed = subject.trim is an almost as ugly workaround. Is there any way to get the pattern match variables trimmed immediately in the case statement?
You can define your own Trimmed extractor:
object Trimmed {
def unapply(s: String): Option[String] = Some(s.trim)
}
And then use it like so:
def isCryptoExample(parts: Seq[Any]): Boolean = parts match {
case List(loneWord) => false
case List(Trimmed(subject), Trimmed(verb)) =>
((subject startsWith "Alice") || (subject startsWith "Bob")) &&
((verb == "encrypts") || (verb == "decrypts"))
}
Daniel Westheide has an excellent introduction to what extractors are and how they work.
A couple more alternatives to go with Emil's:
val Subject = """\s*(Alice|Bob)\s*""".r
val Verb = """\s*(encrypts|decrypts)\s*""".r
List("Alice", " encrypts ") match {
case List(Subject(s), Verb(v)) => true
case _ => false
}
val ss = List("Alice", "Bob")
val vs = List("encrypts", "decrypts")
List("Alice", " encrypts ") match {
case (List(s, v)) if ss.contains(s.trim) && vs.contains(v.trim) => true
case _ => false
}

Scala pattern matching variable binding

Why can't I bind the variable in #-style when the extractor return Option[<Type>]? I.e. this one does not work:
object IsUpperCase {
def unapply(s: String): Option[String] = {
if (s.toUpperCase() == s) {
Some(s)
} else {
None
}
}
}
val s = "DuDu#qwadasd.ru"
s match {
case u # IsUpperCase() => println("gotcha!") // what? "wrong number of arguments for object IsUpperCase"?
case _ =>
}
But this one works!
val s = "DuDu#qwadasd.ru"
s match {
case IsUpperCase(u) => println("gotcha!")
case _ =>
}
From the other hand, if IsUpperCase looks like this:
object IsUpperCase {
def unapply(s: String): Boolean = {
return s.toUpperCase() == s
}
}
Then the first example works, and the second does not! Why is it this way?
what? "wrong number of arguments for object IsUpperCase"?
case u # IsUpperCase() => println("gotcha!")
Well, yes. The return type of unapply is Option[String], which means the pattern match of IsUpperCase must accept a parameter, like this:
case u # IsUpperCase(_) => println("gotcha!") // I don't care about the parameter
The unapply definition that fits the first pattern is this:
object IsUpperCase {
def unapply(s: String): Boolean = s.toUpperCase() == s
}
That can be used to pattern match against IsUpperCase().
Because for the first example you need to write something like case u # IsUpperCase(v) => or case u # IsUpperCase(_) =>, which means "match IsUpperCase(v) and if it succeeds bind the original string to u".

How Can I Check an Object to See its Type and Return A Casted Object

I have method to which I pass an object. In this method I check it's type and depending on the type I do something with it and return a Long. I have tried every which way I can think of to do this and I always get several compiler errors telling me it expects a certain object but gets another. Can someone please explain to me what I am doing wrong and guide me in the right direction? What I have tried thus far is below:
override def getInteger(obj:Object) = {
if (obj.isInstanceOf[Object]) null
else if (obj.isInstanceOf[Number])
(obj:Number).longValue()
else if (obj.isInstanceOf[Boolean])
if (obj:Boolean) 1 else 0
else if (obj.isInstanceOf[String])
if ((obj:String).length == 0 | (obj:String) == "null")
null
else
try {
Long.parse(obj:String)
} catch {
case e: Exception => throw new ValueConverterException("value \"" + obj.toString() + "\" of type " + obj.getClass().getName() + " is not convertible to Long")
}
}
Pattern matching would make it much more nicer.
def getInteger(obj: Any) = obj match {
case n: Number => n.longValue
case b: Boolean => if(b) 1 else 0
case s: String if s.length != 0 && s != "null" => s.toLong
case _ => null
}
This code cries out for using a match:
obj match {
case n: Number => n.longValue
case b: Boolean => if (b) 1 else 0
case s: String => if ((s eq null) || s.length == 0) null else {
// try ... catch ... etc.
}
case o: Object => null
}
Followed my own advice from my comment to my original reply...
This might be a start:
def getInteger (o : Any) : Long = o match {
case (o: Boolean) => if (o) 1 else 0
case (l: Long) => l
case (s: String) => java.lang.Long.parseLong (s)
case _ => 0L
}
(I don't have something to override, and skipped the try/catch for brevity)

Is it a rule that unapply will always return an Option?

I tried to create an unapply method to use in pattern matching, and I tried to make it return something different than Option, however, Eclipse shows that as an error. Is it a rule that unapply must return an Option[T] ?
EDIT: here's the code I'm trying to use. I switched the code from the previous section so that unapply returns a Boolean
import java.util.regex._
object NumberMatcher {
def apply(x:String):Boolean = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return matcher.find
}
def unapply(x:String):Boolean = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return matcher.find
}
}
object x {
def main(args : Array[String]) : Unit = {
val strings = List("geo12","neo493","leo")
for(val str <- strings) {
str match {
case NumberMatcher(group) => println(group)
case _ => println ("no")
}
}
}
}
Eclipse says wrong number of arguments for object NumberMatcher. Why is that?
If you want to return something with unapply, return it inside Some. Returning Boolean just tests if the match can be made or not.
Here is how a pattern matching is translated:
str match {
case NumberMatcher(group) => println(group)
case _ => println("no")
}
Assuming NumberMatcher returns Option[...], it will do:
val r1 = NumberMatcher.unapply(str)
if (r1 != None) {
val group = r1.get
println(group)
} else {
println("no")
}
If NumberMatcher returns Boolean, then you can have it receive something. In that case, this is what happens:
str match {
case NumberMatcher() => println("yes")
case _ => println("no")
}
becomes
val r1 = NumberMatcher.unapply(str)
if (r1) {
println("yes")
} else {
println("no")
}
Note that this is a very superficial explanation. Case matches can test for constants, have additional guard conditions, alternatives, use unapply recursively, use unapplySeq, etc. Here I'm only showing very basic usage to address a specific question. I strongly advise searching for a fuller explanation of pattern matching.
Take a look at this example again.
I quote
The return type of an unapply should be chosen as follows:
* If it is just a test, return a Boolean. For instance case even()
* If it returns a single sub-value of type T, return a Option[T]
* If you want to return several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)].
When you defined unapply to return a Boolean, you were indicating that the pattern doesn't have any wildcards to match (or bind). So the case statement for this unextractor should be case NumberMatcher => println(str), and giving it a variable to fill is wrong.
Alternatively, to make case NumberMatcher(group) => println(group) you need to define unapply() to return Option[String]
package com.tutorial.extracters
object ExtracterwithBooleanReturnType extends App {
import java.util.regex._
object NumberMatcher {
def apply(x: String*) = {
x
}
def unapply(x: String): Option[Boolean] = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return Some(matcher.find)
}
}
val strings = NumberMatcher("geo12", "neo493", "leo")
for (str <- strings) {
str match {
case NumberMatcher(group) => println(group)
case _ => println("no")
}
}
}
we can achieve this with above code as well