Pattern Matching Case Match using Scala - scala

Having issues with trying to get my case match to work as expected.
The outcome I am looking for is as follows:
case 1 OR 2 => randomly select one reference
case any other number above 2 => randomly select (number - 2) reference
case None => throw exception
Im having problems implementing this. so far I have:
val randomList: List = actualList.size match {
case 1 => scala.util.Random.shuffle(actualList).take(1)
case x? => scala.util.Random.shuffle(actualList).take(2)
case None => throw new IllegalStateException("references have not been generated successfully.")
}
I get an error message with the 'None' stating the pattern type is incompatible with expected type Int.
If there is a better way to implement this, please do share.
Any help would be much appreciated.
Thanks

I think you can shuffle right away to simplify each expression in case clauses:
val actualList = List(1, 2, 3)
val shuffled = Random.shuffle(actualList)
shuffled.size match {
case 0 => throw new RuntimeException()
case 1 | 2 => shuffled.take(1)
case _ => shuffled.take(2)
}

You can use |, guard and _ to achieve this
val randomList: List = actualList.size match {
case 0 => throw new IllegalStateException("references have not been generated successfully.")
case 1 | 2 => scala.util.Random.shuffle(actualList).take(1)
case _ => scala.util.Random.shuffle(actualList).take(2)
}

Related

how to print the index of failure in catch expression in scala?

I have a code looking like this:
import scala.util.{Try, Success, Failure}
Try(
for (i <- 1 to 1000) {
doSomething(df(i))
}
) match {
case Success(t) => println(s"success")
case Failure(t) => println(s"failure")
}
i want to print the index of the failed input. how to print the index i in catch expression?
You can do this instead using Cats:
import scala.util.Try
import cats.implicits._
(1 to 1000).traverse(i => Try(doSomething(df(i))).toEither.left.map(ex => (ex, i))) match {
case Right(_) => println("success")
case Left((ex, i)) => println(s"failure: ${ex.getMessage} on idx: ${i}")
}
If you do not want to use Cats, you can just:
val attempts = for {
i <- Stream.range(start = 1, end = 1000) // Thanks to Bogdan for the idea of using a Stream.
} yield Try(doSomething(df(i))).toEither.left.map(ex => (ex, i))
attempts.collectFirst { case Left((ex, i)) => ex -> i } match {
case None => println("success")
case Some((ex, i)) => println(s"failure: ${ex.getMessage} on idx: ${i}")
}
You should definitely follow Luis's answer, but to address your comment, you could also catch IllegalArgumentException and re-throw it with the added index to the message, perhaps something like so:
Try(
for (i <- 1 to 1000) {
try doSomething(i) catch { case e: IllegalArgumentException => throw new IllegalArgumentException(s"Failed with index $i", e)}
}
) match {
case Success(t) => println(s"success")
case Failure(t) => println(s"failure", t)
}
However this seems hideous, and I do not advise it.
IMO the question hints that the code is lying.
you could write the code differently:
import scala.util.{Try, Success, Failure}
for (i <- 1 to 1000) {
Try(
doSomething(df(i))
) match {
case Failure(t) => println(s"failure on $i")
case _ =>
}
}
But you don't want to. Why not? Because you want to stop the iteration after the first failure. But you're using loop from 1 to 1000. You don't really intend to do the whole 1000 iterations. You're using an exception to break a for loop.
I would rewrite this code to make it clear that i don't intend to iterate the entire range explicitly.
You could, for example, use find instead of for, to find the index that causes a failure to happen. if None was found -> everything was successful.
so something similar to (untested):
(1 to 1000).indexWhere{index=>Try{doSomething(index)}.isFailure
i'm not sure if it's find or indexWhere in scala but you get the idea.
if you would like to obtain the exception as well, not just the index you could use views (https://docs.scala-lang.org/overviews/collections/views.html) to change your sequence to a lazily evaluated one, map the list to a tuple of form (index, Try) (without iterating the entire collection, due to lazyness of .view result), and then collectFirst where second element of tuple is Failure.
so something like (untested):
(1 to 1000).view.map{index => (index, doSomething(index)}.collectFirst{case (i,Failure(e)) => println(s"error was $e at index $i")}
alternatively you could write a very very small recursion to iterate the index sequence (also untested)
def findException(indexes: Seq[Int]): Option[(Int, Exception)] = indexes match {
case Nil => None
case index+:remaining =>
Try(doSomething(i)) match {
case Success(_) => findException(remaining)
case Failure(e) => Option((index,e))
}
findException(1 to 1000).map(println)
one question is how did you determine 1 to 1000?
this question would look differently if you had a collection of elements to verify, and not a range. in that case you would probably just use foldLeft.

Scala type mismatch while doing pattern matching

I am trying to do a pattern matching which is working as expected but IntelliJ IDE is showing the below highlighting error
Expected: PartialFunction[Regex, NotInferedB], actual: Nothing => Boolean.
Cannot resolve method x.unapply
Cannot resolve symbol x
Code:
scala> val keys = Map(
| "XYZ" -> List("(?i)(.*SANDWICH.*)",
| "(?i)(.*BURGER.*)").map(x => x.r)
| )
keys: scala.collection.immutable.Map[String,List[scala.util.matching.Regex]] = Map(XYZ -> List((?i)(.*SANDWICH.*), (?i)(.*BURGER.*)))
scala> val desc = "I DON't LIKE SANDWICH "
desc: String = "I DON't LIKE SANDWICH "
scala> if (keys("XYZ").collect{x => desc match{ case x(key) => true}}.contains(true)) println("yes") else println("no")
yes
The error is showing in the collect part. Can anyone please guide me as what might be the issue that is causing the highlighting error in IntelliJ
The reason you are getting PartialFunction[Regex, NotInferedB], actual: Nothing => Boolean compile time because you are giving lambda function whereas the collect function takes the PartialFunction.
I don't know what you wanted to achieve with the snippet of code but below will resolve your problem.
if (keys("XYZ").collect { case x => true }.contains(true)) println("yes") else println("no")
This is the right way to write you last expression is:
if (keys("XYZ").collect{case x: Regex => desc match{ case x(_) => true}
}.contains(true)) println("yes") else println("no")
But there is a better way to write it :
if(keys("XYZ").exists { x => x.findFirstMatchIn(desc) match {
case Some(_) => true
case None => false
}
}) println("yes") else println("no")
Try this:
if (keys("XYZ").exists(_.findFirstIn(desc).isDefined)) println("yes") else println("no")

condition matching in an array with case class in scala

I have a task need to find a particular string in an array:
1. if found, return its value;
2. if not found, return -1.
I wrote an "idea" code, but I don't know how to finish it correctly.
case class person(name:String, value: Int)
personList[Array[person]]
val result = personList match {
case x if x.name == "john" => x.value
case _ => -1 }
the complier reports errors at "case x if x.name"
Would this work for you?
persons.find(_.name == "john").fold(-1)(_.value)
Note: I've left the creation and/or population of the persons array up to you.
val result = personList.find(_.name=="john") match {
case some(x) => x.value
case None => -1 }

map expression in case clause in scala pattern matching

I have a configuration value that matches to one of the values in a map and depending on to which it matches i take an action. Here is some sample code of what i am trying to do
val x = 1 // or 2 or 3
val config = Map("c1"-> 1, "c2"-> 2, "c3"-> 3)
x match {
case config("c1") =>
println("1")
case config("c2") =>
println("2")
case config("c3") =>
println("3")
}
Now this should print 1 because config("c1") evaluates to 1 but it gives error
error: value config is not a case class, nor does it have an unapply/unapplySeq member
case config("c1") =>
Similarly for the other 2 cases. Why should i have an unapply here? Any pointers?
An expression like that looks like an extractor, hence the message about unapply/unapplySeq methods. If you don't want to use an extractor but just want to match against a plain value, you need to store that value in a stable identifier - you can't use an arbitrary expression as a match case:
val case1 = config("c1")
x match {
case case1 => println("1")
...
}
To the best of my knowledge, in Scala, x match {case config("c1") gets translated to config.unapply(x) with the branching dependent on the result of the unapply method. As Imm already mentioned in his answer, this isn't the case for stable identifiers (literals and val), and I'd encourage you to use his solution.
Nevertheless, to show you how you could solve the problem using extractors, I'd like to post a different solution:
def main(args: Array[String]): Unit = {
object config {
val configData = Map("c1" -> 1, "c2" -> 2, "c3" -> 3)
def unapply(value: Int): Option[String] = configData find (_._2 == value) map (_._1)
}
1 to 4 foreach {
case config("c1") => println("1")
case config("c2") => println("2")
case config("c3") => println("3")
case _ => println("no match")
}
}
I changed the match for a foreach to show the different results, but this has no effect on the implementation. This would print:
1
2
3
no match
As you can see, case config("c1") now calls the unapply method and checks whether the result is Some("c1"). Note that this is inverse to how you'd use a map: The key is searched according to the value. However, this makes sense: If in the map, "c1" and "c2" both map to 1, then 1 matches both, the same way _ matches everything, in our case even 4 which is not configured.
Here's also a very brief tutorial on extractors. I don't find it particularly good, because both, the returned type and the argument type are Int, but it might help you understand what's going on.
As others have stated, with x match { case config("c1") => ..., scala looks for an extractor by the name of config (something with an unapply method that takes a single value and returns an Optional value); Making pattern matching work this way seems like an abuse of the pattern, and I would not use an extractor for this.
Personally, I would recommend one of the following:
if (x == config("c1"))
println("1")
else if (x == config("c2"))
println("2")
else ...
Or, if you're set on using a match statement, you can use conditionals like this:
x match {
case _ if x == config("c1") =>
println("1")
case _ if x == config("c2") =>
println("2")
case _ if x == config("c3") =>
println("3")
}
Not as clean; unfortunately, there isn't a way to invoke a method call literally where the extractor goes. You can use back-ticks to tell scala "match against the value of this variable" (rather than default behavior, which would yield the value named as that variable):
val (c1,c2,c3) = (config("c1"), config("c2"), config("c3"))
x match {
case `c1` =>
println("1")
case `c2` =>
println("2")
case `c3` =>
println("3")
}
Finally, if your goal is to reverse-apply a map, maybe try this instead?
scala> Map("a" -> 1).map { case (k,v) => (v,k) }
res0: scala.collection.immutable.Map[Int,String] = Map(1 -> a)

Pattern Matching Pass Through in Scala

I want to do something like this:
val a = v match {
case 1 => 1
case 2 if (condition) => logging
case 2 if (other conditions) => 3
case 2 if (more conditions) => 4
case _ => 5
}
I want this to return just log for first case 2, but fall through otherwise to see what gets returned
edit: updated
Scala's case matching doesn't "fall through", but if I understand correctly, this will do what you want.
val a = v match {
case 1 => 1
case 2 => {
logging // I'm assuming `logging` is some Unit
if(some condition) 3
else if(some other condition) 4
else 5
}
case _ => 5
}
Something like this?
If v == 2, a will be assigned logging otherwise a will be assigned the value of v
val a = v match {
case 2 => logging
case _ => v
}