scala pattern matching on value in sequence of strings - scala

variable someKey can be either "a", "b" or "c".
I can do this:
someKey match {
case "a" => someObjectA.execute()
case "b" => someOther.execute()
case "c" => someOther.execute()
case _ => throw new IllegalArgumentException("Unknown")
}
how can I compress this pattern matching so I can check if someKey with e.g. Seq("b", "c") and if it is in the sequence then replace two lines of pattern match with one?
EDIT:
someKey match {
case "a" => someObjectA.execute()
case someKey if Seq("b","c").contains(someKey) => someOther.execute()
case _ => throw new IllegalArgumentException("Unknown")
}

You can have "or" in the case clause:
someKey match {
case "a" => someObjectA.execute()
case "b"|"c" => someOther.execute()
case _ => ???
}

For this particular case, I'd probably go to
// likely in some companion object so these get constructed once
val otherExecute = { () => someOther.execute() }
val keyedTasks = Map(
"a" -> { () => someObjectA.execute() },
"b" -> otherExecute,
"c" -> otherExecute
)
// no idea on the result type of the execute calls? Unit?
def someFunction(someKey: String) = {
val resultOpt = keyedTasks.get(someKey).map(_())
if (resultOpt.isDefined) resultOpt.get
else throw new IllegalArgumentException("Unknown")
}

Related

in scala why does for yield return option instead of string

I'm new to scala I'm trying to understand for/yield and don't understand why the following code returns an option not a String
val opString: Option[String] = Option("test")
val optionStr : Option[String] = for {
op <- opString
} yield {
opString match {
case Some(s) => s
case _ => "error"
}
}
A for-expression is syntactic sugar for a series of map, flatMap and withFilter calls. Your specific for-expression is translated to something like this:
opString.map(op => opString match {
case Some(s) => s
case _ => "error"
})
As you can see, your expression will just map over opString and not unwrap it in any way.
Desugared expression for your for ... yield expression is:
val optionStr = opString.map {
op =>
opString match {
case Some(s) => s
case _ => "error"
}
}
The type of opString match {...} is String, so the result type of applying map (String => String) to Option[String] is Option[String]
What you're looking for is getOrElse:
opString.getOrElse("error")
This is equivalent to:
opString match {
case Some(s) => s
case _ => "error"
}

Task not serializable exception in dataframe map function

I need to convert the datatypes of columns in dataframe and catch all data type conversion failures. I have tried the below option but it throws "Task not serializable".
var errorListBuffer = new ListBuffer[Map[String, String]]()
df.map(r => {
val value = r.getAs(columnName).toString
val index = r.fieldIndex(columnName)
Try {
val cleanValue = value match {
case n if r.isNullAt(index) => null
case x => x.trim
}
new_type match {
case "date" => new SimpleDateFormat("yyyy-MM-dd").format(new SimpleDateFormat(dateFormat).parse(cleanValue))
case "datetime" => new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new SimpleDateFormat(dateFormat).parse(cleanValue))
case "string" => toLower match {
case "1" => cleanValue.toLowerCase
case _ => cleanValue
}
case _ => cleanValue
}
} match {
case Success(v) => org.apache.spark.sql.Row.fromSeq(r.toSeq ++ v)
case Failure(e) => errorListBuffer += Map(
LOADER_COLUMN_NAME -> columnName,
LOADER_LEVEL -> "ERROR",
LOADER_ERROR_MESSAGE -> e.getMessage,
LOADER_RECORD_UUID -> r.getAs(LOADER_UUID).toString)
org.apache.spark.sql.Row.fromSeq(r.toSeq ++ null)
}
})
var dfnew = sqlContext.createDataFrame(df, schema)
Please let me know how I can resolve this.

Is it possible to print values during collect without modifying return type?

I have a code segment something like this:
def test() : Seq[Int] =
List("A", "B", "C") collect {
case "A" => 1
case "B" => 2
//case _ => println(_)
}
Now I would like to print specific values (just for debugging) on the output without adding any elements to the resulting collection. If I uncomment the commented line, Scala infers the value of the expression to Seq[Any], which is completely understandable.
Anybody got any hints how to do this? Thanks in advance!
def skipped: Nothing = throw new Exception("Oops, not skipped after all!")
List("A", "B", "C") collect {
case "A" => 1
case "B" => 2
case x if { println(x); false } => skipped
}
object PrintSkip {
def unapply(a: Any): Option[Any] = {
println(a)
None
}
}
List(Some("A"), None, Some("C")) collect {
case Some("A") => 1
case None => 2
case Some(PrintSkip(_)) => skipped
}
flatMap
List("A", "B", "C") flatMap {
case "A" => List(1)
case "B" => List(2)
case x => println(x); Nil
}
collect/flatten
List("A", "B", "C").collect {
case "A" => Some(1)
case "B" => Some(2)
case x => println(x); None
}.flatten
With collect, no need for wrapping things in an Option.
List("A", "B", "C").collect(new PartialFunction[String, Int] {
def apply(s:String):Int = s match {
case "A" => 1
case "B" => 2
}
def isDefinedAt(s:String) = {
try {
apply(s)
true
} catch {
case e:MatchError => { println(s); false }
}
}
})

Pattern matching with more than one match

Consider the following Scala code.
val a = "both"
a match {
case "both" | "foo" => println ("foo") // case 1
case "both" | "bar" => println ("bar") // case 2
}
I would like match to work so that if a == "both", Scala will execute both cases. Is this possible or are there any alternatives to achieve what I want?
Standard pattern-matching will always match on only exactly one case. You can get close to what you want by using the fact that patterns can be treated as partial functions (see the Language Specification, Section 8.5, Pattern Matching Anonymous Functions) and by defining your own matching operator, though:
class MatchAll[S](scrutinee : =>S) {
def matchAll[R](patterns : PartialFunction[S,R]*) : Seq[R] = {
val evald : S = scrutinee
patterns.flatMap(_.lift(evald))
}
}
implicit def anyToMatchAll[S](scrut : =>S) : MatchAll[S] = new MatchAll[S](scrut)
def testAll(x : Int) : Seq[String] = x matchAll (
{ case 2 => "two" },
{ case x if x % 2 == 0 => "even" },
{ case x if x % 2 == 1 => "neither" }
)
println(testAll(42).mkString(",")) // prints 'even'
println(testAll(2).mkString(",")) // prints 'two,even'
println(testAll(1).mkString(",")) // prints 'neither'
The syntax is slightly off the usual, but to me such a construction is still a witness to the power of Scala.
Your example is now written as:
// prints both 'foo' and 'bar'
"both" matchAll (
{ case "both" | "foo" => println("foo") },
{ case "both" | "bar" => println("bar") }
)
(Edit huynhjl pointed out that he gave a frighteningly similar answer to this question.)
At risk of being Captain Obvious, in a case like this it would be simplest just to forget pattern matching and use if.
if (a == "both" || a == "foo") println("foo")
if (a == "both" || a == "bar") println("bar")
If the repetition of a == worries you, you could instead write
if (Set("both", "foo")(a)) println("foo")
if (Set("both", "bar")(a)) println("bar")
using the fact that the apply method on Set does the same as contains, and is a bit shorter.
match executes one, and only one, of the cases, so you can't do this as an or in the match. You can, however, use a list and map/foreach:
val a = "both"
(a match {
case "both" => List("foo", "bar")
case x => List(x)
}) foreach(_ match {
case "foo" => println("foo")
case "bar" => println("bar")
})
And you're not duplicating any of the important code (in this case the printlns).
Just match twice:
val a = "both"
a match {
case "both" | "foo" => println ("foo") // Case 1
}
a match {
case "both" | "bar" => println ("bar") // Case 2
}
One possible way could be:
val a = "both"
a match {
case "foo" => println ("foo") // Case 1
case "bar" => println ("bar") // Case 2
case "both" => println ("foo"); println ("bar")
}

Scala: Pattern matching when one of two items meets some condition

I'm often writing code that compares two objects and produces a value based on whether they are the same, or different, based on how they are different.
So I might write:
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case (Some(value), None)) => "b"
case (None, Some(value)) => "b"
case _ = > "c"
}
Those 2nd and 3rd cases are the same really, so I tried writing:
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case (Some(value), None)) || (None, Some(value)) => "b"
case _ = > "c"
}
But no luck.
I encounter this problem in a few places, and this is just a specific example, the more general pattern is I have two things, and I want to know if one and only one of them meet some predicate, so I'd like to write something like this:
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case OneAndOnlyOne(value, v: Option[Foo] => v.isDefined ) => "b"
case _ = > "c"
}
So the idea here is that OneAndOnlyOne can be configured with a predicated (isDefined in this case) and you can use it in multiple places.
The above doesn't work at all, since its backwards, the predicate needs to be passed into the extractor not returned.
How about something like this?
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case new OneAndOnlyOne(v: Option[Foo] => v.isDefined )(value) => "b"
case _ = > "c"
}
with:
class OneAndOnlyOne[T](predicate: T => Boolean) {
def unapply( pair: Pair[T,T] ): Option[T] = {
val (item1,item2) = pair
val v1 = predicate(item1)
val v2 = predicate(item2)
if ( v1 != v2 )
Some( if ( v1 ) item1 else item2 )
else
None
}
}
But, this doesn't compile.
Can anyone see a way to make this solution work? Or propose another solution? I'm probably making this more complicated than it is :)
I think you're asking two slightly different questions.
One question is how to use "or" in switch statements. || doesn't work; | does. And you can't use variables in that case (because in general they might match different types, which renders the type confusing). So:
def matcher[T](a: (T,T)) = {
a match {
case (Some(x),Some(y)) => "both"
case (Some(_),None) | (None,Some(_)) => "either"
case _ => "none"
}
}
Another question is how to avoid having to do this over and over, especially if you want to be able to get at the value in the tuple. I've implemented a version here for Option, but you could use an unwrapped tuple and a boolean.
One trick to achieve this is that to prewrap the values before you start matching on it, and then use your own matching constructs that do what you want. For instance,
class DiOption[+T] {
def trinary = this
}
case class Both[T](first: T, second:T) extends DiOption[T] { }
case class OneOf[T](it: T) extends DiOption[T] { }
case class Neither() extends DiOption[Nothing] { }
implicit def sometuple2dioption[T](t2: (Option[T],Option[T])): DiOption[T] = {
t2 match {
case (Some(x),Some(y)) => Both(x,y)
case (Some(x),None) => OneOf(x)
case (None,Some(y)) => OneOf(y)
case _ => Neither()
}
}
// Example usage
val a = (Some("This"),None)
a trinary match {
case Both(s,t) => "Both"
case OneOf(s) => "Just one"
case _ => "Nothing"
}
If you have to support arbitrary predicates you can derive from this (which is based on Daniel's idea):
List(v1, v2) filter (_ %2 == 0) match {
case List(value1, value2) => "a"
case List(value) => "b"
case _ => "c"
}
the definition of the function:
def filteredMatch[T,R](values : T*)(f : T => Boolean)(p: PartialFunction[List[T], R]) : R =
p(List((values filter f) :_* ))
Now you can use it like this:
filteredMatch(v1,v2)(_ %2 == 0){
case List(value1, value2) => "a"
case List(value) => "b"
case _ => "c"
}
I'm not so sure if it's a good idea (i.e. readable). But a neat exercise nonetheless.
It would be nice if you could match on tuples: case (value1, value2) => ... instead of lists.
How about this:
Welcome to Scala version 2.8.0.r20327-b20091230020149 (Java HotSpot(TM) Client VM, Java 1.6.0_17).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def m(v1: Any,v2: Any) = (v1,v2) match {
| case (Some(x),Some(y)) => "a"
| case (Some(_),None) | (None,Some(_)) => "b"
| case _ => "c"
| }
m: (v1: Any,v2: Any)java.lang.String
scala> m(Some(1),Some(2))
res0: java.lang.String = a
scala> m(Some(1),None)
res1: java.lang.String = b
scala> m(None,None)
res2: java.lang.String = c
scala>
You should be able to do it if you define it as a val first:
val MyValThatIsCapitalized = new OneAndOnlyOne(v: Option[Foo] => v.isDefined )
val result = (v1,v2) match {
case (Some(value1), Some(value2)) => "a"
case MyValThatIsCapitalized(value) => "b"
case _ = > "c"
}
As implied by the name, the name of the val containing the extractor object must be capitalized.
On Scala 2.8:
val result = List(v1,v2).flatten match {
case List(value1, value2) => "a"
case List(value) => "b"
case _ = > "c"
}
On Scala 2.7, however, you need a type hint to make it work. So, assuming value is Int, for instance, then:
val result = (List(v1,v2).flatten : List[Int]) match {
case List(value1, value2) => "a"
case List(value) => "b"
case _ = > "c"
}
The funny thing about it is that I misread "first" as "list" on Mitch Blevins answer, and that gave me this idea. :-)
Since you already matched against (Some(x), Some(y)), you may match against (None, None) explicitly, and the remaining cases are (Some(x), None) and (None, Some(y)):
def decide [T](v1: Option[T], v2:Option[T]) = (v1, v2) match {
case (Some (x), Some (y)) => "a"
case (None, None) => "c"
case _ => "b"
}
val ni : Option [Int] = None
decide (ni, ni) // c
decide (Some (4), Some(3)) // a
decide (ni, Some (3)) // b
decide (Some (4), ni) // b