I have several conditions that I want to stack in a scala match statement. In another language I could just omit the 'break' between the cases of a switch statement.
I know I can combine raw alternatives separated by a pipe character, but can't seem to find how to combine guarded cases, e.g.:
val isAdmin = true
myval match {
case 1 if isAdmin => ...
case 2 if isAdmin => ...
case 1 | 2 => // this is fine but doesn't apply the guard, so no use
case 1 | 2 if isAdmin => // doesn't apply the guard to '1'
case 1 if isAdmin | 2 if isAdmin => // invalid syntax
}
Is it possible to combine the first two cases somehow?
The guard statement applies to everything, for good or for ill. This means that your example
case 1 | 2 if isAdmin => ...
actually does what you want and what you say it does not, but it also means that
Option("Hi") match {
case Some(x) | None if x == "Hi" => 1
case _ => 0
}
does not work. (In fact, it doesn't even compile.)
Fortunately, Scala lets you drop a def in practically anywhere.
def caseNice = 1
Option("Hi") match {
case Some(x) if x.length < 3 => caseNice
case None => caseNice
case _ => 0
}
which is how you should deal with common functionality that is difficult invoke from a single case statement.
Related
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)
}
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)
Consider
val b = ParArray("a","b","c")
However, on pattern matching b for instance as follows,
b match {
case ParArray(_,"b",_) => 2
case _ => -1
}
<console>:11: error: object ParArray is not a case class,
nor does it have an unapply/unapplySeq member
case ParArray(_,"b",_) => 2
Thus how to pattern match ParArray ?
Thanks in Advance.
ParArray is a parallel structure. Its main usecase is to operate on the elements concurrently (see docs). That's done (roughly speaking) by concurrently applying operations on arbitratry segments of the ParArray.
For the sake of a counter example, imagine that you could unapply on ParArray. How would the match look like:
Given val b = ParArray("a","b","c","d")
b match {
case ParArray(_,"b",_) => 2
case _ => -1
}
Could be arbitrarily converted into:
//Parallel split & join:
"a","b" match {
case ParArray(_,"b",_) => 2
case _ => -1
}
"c","d" match {
case ParArray(_,"b",_) => 2
case _ => -1
}
Which of course, doesn't make sense.
For the specific case on your question, you could use direct addressing:
if (b.size == 3 && b(1)=="b") 2 else -1
In a more generic sense, a good way of reasoning is thinking of how your operation could be applied to all elements at the same time.
If you need to match on structure, you could obtain an array with the parArray.toArray method. You should also question why is a ParArray being used.
These two statements behave the same :
def getNum(inp: String): Double = inp match { case "" | null => 0.0 case _ => inp.toDouble }
def getNum(inp: String): Double = inp match { case "" | null => 0.0 case x => x.toDouble }
Question is, where should either one be used and is one essentially better than the other?
The bytecode for the two is identical, so you can use whichever you prefer stylistically.
Note that in some cases you may have a complex expression as the source of your value to match, which makes it harder to refer to. Thus you may have greater consistency of style with the case x => x.toDouble form.
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
}