Unexpected behaviour with pattern matching on List - scala

Why the following code print one in both cases.
oneOrList(o: Any) = o match {
case List => "list"
case _ => "one"
}
println(oneOrList(1))
println(oneOrList(List(1, 2)))

The List in your case List => is not a type discrimination, it's a test of the target of the match (technically called the scrutinee) against the singleton List object used mainly to provide the List(v1, v2, v3, ...) factory via its apply method.

case List => "list" would match only against List singleton object. So it would match only oneOrList(List)
To match against lists use this:
oneOrList(o: Any) = o match {
case x: List[_] => "list"
case _ => "one"
}
println(oneOrList(1))
println(oneOrList(List(1, 2)))

Related

Scala 2.13: avoid repeated case class conversion?

I have a pattern matching for sealed trait which expects two of one case classes as shown below:
expressions.head match {
case SingleValueExpression(value,_operator,_ignoreCase) => filter ++ FromScala(
SingleValueExpression(value,_operator,_ignoreCase)
).transform(ToJson.string)
case MultipleValueExpression(value,_operator,_apply,_instances) => filter ++ FromScala(
MultipleValueExpression(value,_operator,_apply,_instances)
).transform(ToJson.string)
}
You can see that even after decoding the case classes, I am recreating the case class in the next step:
case MultipleValueExpression(value,_operator,_apply,_instances) => MultipleValueExpression(value,_operator,_apply,_instances)
Is there a way to match the pattern such that I could check if the instance is of that case class and then use the value as it is instead of destructuring it and recreating the same case class?
To just check the type in a pattern match, use :, like so:
expressions.head match {
case sve: SingleValueExpression => filter ++ FromScala(sve).transform(ToJson.string)
case mve: MultipleValueExpression => filter ++ FromScala(mve).transform(ToJson.string)
}
You could even have something like
val fromScala =
expressions.head match {
case sve: SingleValueExpression => FromScala(sve)
case mve: MultipleValueExpression => FromScala(mve)
}
filters ++ fromScala.transform(ToJson.string)
You can alternatively use # to do extraction and binding in one case (e.g. you want to match based on a component of a case class but save the overall match):
case sve # SingleValueExpression(value, _, _) if somePredicate(value) =>
// sve is the overall SingleValueExpression, and value is also available in this branch

separate scala list based on matching pattern

i have a list of the following scala trait. How can i separate the list into two, one containing only ValidatedSbcCommand objects and other only containing FailedValidationSbcCommand objects?
sealed trait SbcCommandorOrValidationError
case class ValidatedSbcCommand(sbcCommand: SbcCommand) extends SbcC ommandorOrValidationError
case class FailedValidationSbcCommand(sbcCommandError: SbcCommandError) extends SbcCommandorOr
Use the partition method on list. It takes a predicate and produces a (List, List) The first list is for the true case the second is for false.
val result = originalList.foldRight(Tuple2(List[ValidatedSbcCommand](), List[FailedValidationSbcCommand]())){ (start, rest) =>
start match {
case a:ValidatedSbcCommand => (a::rest._1, rest._2)
case b:FailedValidationSbcCommand => (rest._1, b::rest._2)
case _ => rest
}
}
Then result._1 will give you a list of ValidatedSbcCommand, and result._2 will give you a list of FailedValidationSbcCommand.
I prefer using partition with pattern matching. Given list is of type List[SbcCommandorOrValidationError] and contains only ValidatedSbcCommands and FailedValidationSbcCommands, you can do this:
val (validatedCommands, failedCommands) = list.partition {
case command: ValidatedSbcCommand => true
case _ => false
}
This will return a tuple of type (List[SbcCommandorOrValidationError], List[SbcCommandorOrValidationError]) where the first list is all the ValidatedSbcCommands and the second is all the FailedValidationSbcCommands.
If you need to access the specific subclass later on, don't cast. Use pattern matching as above:
validatedCommands.map {
case c: ValidatedSbcCommand => functionTakingValidatedSbcCommandsOnly(c)
}
From Scala 2.13, you can use of partitionMap, which does exactly what you want, keeping the subtype info:
list partitionMap {
case v: ValidatedSbcCommand => Left(v)
case f: FailedValidationSbcCommand => Right(f)
}

Retrieve tuple from string

I have the following input string:
"0.3215,Some(0.5123)"
I would like to retrieve the tuple (0.3215,Some(0.5123)) with: (BigDecimal,Option[BigDecimal]).
Here is one of the thing I tried so far:
"\\d+\\.\\d+,Some\\(\\d+\\.\\d+".r findFirstIn iData match {
case None => Map[BigDecimal, Option[BigDecimal]]()
case Some(s) => {
val oO = s.split(",Some\\(")
BigDecimal.valueOf(oO(0).toDouble) -> Option[BigDecimal](BigDecimal.valueOf(lSTmp2(1).toDouble))
}
}
Using a Map and transforming it into a tuple.
When I try directly the tuple I get an Equals or an Object.
Must miss something here...
Your code has several issues, but the big one seems to be that the case None side of the match returns a Map but the Some(s) side returns a Tuple2. Map and Tuple2 unify to their lowest-common-supertype, Equals, which is what you're seeing.
I think this is what you're trying to achieve?
val Pattern = "(\\d+\\.\\d+),Some\\((\\d+\\.\\d+)\\)".r
val s = "0.3215,Some(0.5123)"
s match {
case Pattern(a,b) => Map(BigDecimal(a) -> Some(BigDecimal(b)))
case _ => Map[BigDecimal, Option[BigDecimal]]()
}
// Map[BigDecimal,Option[BigDecimal]] = Map(0.3215 -> Some(0.5123))

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)

Scala Map pattern matching

How to do pattern matching on a Map in Scala ?
A (non working) attempt includes,
Map("a"->1, "b"->2, "c"->3) match {
case Map(a,b,_*) => a
}
which errs with
value Map is not a case class, nor does it have an unapply/unapplySeq member
case Map(a,b,_*) => a
The error is indicative enough, yet how to enrich Map with an unapply method for pattern matching ?
Many Thanks
Update
Following #Paul's comment, a neater use case may be like this,
Map("a"->1, "b"->2, "c"->3) match {
case Map("b"->2,_*) => "222"
}
namely, in this case, if map contains key b that maps onto value 2.
Most easy way is tramsform Map to List:
Map("a"->1, "b"->2, "c"->3).to[List] match {
case List(a,b,_*) => a
}
An approach to enriching Map with an unapplySeq method for pattern matching includes this,
object MapExtractor {
def unapplySeq[A <% Ordered[A], B <% Ordered[B]]
(s: Map[A,B]): Option[Seq[(A,B)]] = Some(s.toSeq.sorted)
}
where the sorting approach may be changed to any orderable (items comparable) logic. In this example,
Map("b"->2, "a"->1, "c"->3) match {
case MapExtractor ( x, xs # _* ) => println(s"x: $x") ; println(s"xs: $xs")
}
delivers
x: (a,1)
xs: ArrayBuffer((b,2), (c,3))