Scala case match default value - scala

How can I get the default value in match case?
//Just an example, this value is usually not known
val something: String = "value"
something match {
case "val" => "default"
case _ => smth(_) //need to reference the value here - doesn't work
}
UPDATE: I see that my issue was not really understood, which is why I'm showing an example which is closer to the real thing I'm working on:
val db = current.configuration.getList("instance").get.unwrapped()
.map(f => f.asInstanceOf[java.util.HashMap[String, String]].toMap)
.find(el => el("url").contains(referer))
.getOrElse(Map("config" -> ""))
.get("config").get match {
case "" => current.configuration.getString("database").getOrElse("defaultDatabase")
case _ => doSomethingWithDefault(_)
}

something match {
case "val" => "default"
case default => smth(default)
}
It is not a keyword, just an alias, so this will work as well:
something match {
case "val" => "default"
case everythingElse => smth(everythingElse)
}

The "_" in Scala is a love-and-hate syntax which could really useful and yet confusing.
In your example:
something match {
case "val" => "default"
case _ => smth(_) //need to reference the value here - doesn't work
}
the _ means, I don't care about the value, as well as the type, which means you can't reference to the identifier anymore.
Therefore, smth(_) would not have a proper reference.
The solution is that you can give the a name to the identifier like:
something match {
case "val" => "default"
case x => smth(x)
}
I believe this is a working syntax and x will match any value but not "val".
More speaking. I think you are confused with the usage of underscore in map, flatmap, for example.
val mylist = List(1, 2, 3)
mylist map { println(_) }
Where the underscore here is referencing to the iterable item in the collection.
Of course, this underscore could even be taken as:
mylist map { println }

here's another option:
something match {
case "val" => "default"
case default#_ => smth(default)
}

Related

Scala methods with generic parameter type

I have been working with Scala for close to a year, but every now and then I come across a piece of code that I don't really understand. This time it is this one. I tried looking into documents on "scala methods with generic parameter type", but I am still confused.
def defaultCall[T](featureName : String) (block : => Option[T])(implicit name: String, list:Seq[String]) : Option[T] =
{
val value = block match {
case Some(n) => n match {
case i : Integer => /*-------Call another method----*/
case s : String => /*--------Call another method----*/
}
case _ => None
}
The method is called using the code shown below :
var exValue = Some(10)
val intialization = defaultCall[Integer]("StringName"){exValue}
What I don't understand in the above described code is the "case" statement in the defaultCall method.
I see that when the exValue has a value and is not empty, the code works as expected. But in case I change the exValue to None, then my code goes into the "case _ = None" condition. I don't understand why this happens since the match done here is against the "variable" which would be either an Integer or a String.
What happens here is that when you pass a None it will match on the second case, which "catches" everything that is not an instance of a Some[T]:
block match {
case Some(n) => // Will match when you pass an instance of Some[T]
case _ => // Will match on any other case
}
Note that None and Some are two different classes that inherit from Option.
Also, the variable match is only done if the first match succeeds, otherwise not. To achieve the type checking in the first match you could do:
block match {
case Some(n: Int) => // do stuff
case Some(n: String) => // do stuff
case _ => // Will match on any other case
}
Hope that helps

Pattern matching a domain name

I don't use pattern matching as often as I should.
I am matching a domain name for the following:
1. If it starts with www., then remove that portion and return.
www.stackoverflow.com => "stackoverflow.com"
2. If it has either example.com or example.org, strip that out and return.
blog.example.com => "blog"
3. return request.domain
hello.world.com => "hello.world.com"
def filterDomain(request: RequestHeader): String = {
request.domain match {
case //?? case #1 => ?
case //?? case #2 => ?
case _ => request.domain
}
}
How do I reference the value (request.domain) inside the expression and see if it starts with "www." like:
if request.domain.startsWith("www.") request.domain.substring(4)
You can give the variable you pattern matching a name and Scala will infer its type, plus you can put an if statement in you case expression as follows
def filterDomain(request: RequestHeader): String = {
request.domain match {
case domain if domain.startsWith("www.") => domain.drop(4)
case domain if domain.contains("example.org") | domain.contains("example.com") => domain.takeWhile(_!='.')
case _ => request.domain
}
}
Note that the order of the case expressions matters.
When writing case clauses you can do something like:
case someVar if someVar.length < 2 => someVar.toLowerCase
This should make pretty clear how grabbing matched values works.
So in this case, you would need to write something like:
case d if d.startsWith("www.") => d.substring(4)
If you're dead set on using a regex rather than String methods such as startsWith and contains, you can do the following:
val wwwMatch = "(?:www\\.)(.*)".r
val exampleMatch = "(.*)(?:\\.example\\.(?:(?:com)|(?:org)))(.*)".r
def filterDomain(request: String): String = {
request.domain match {
case wwwMatch(d) => d
case exampleMatch(d1, d2) => d1 + d2
case _ => request.domain
}
}
Now, for maintainability's sake, I wouldn't go this way, because a month later, I will look at this and not remember what it's doing, but that's your call.
you don't need pattern matching for that:
request.domain
.stripPrefix("www.")
.stripSuffix(".example.org")
.stripSuffix(".example.com")

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 match case on regex directly

I am trying to do something like the following:
list.foreach {x =>
x match {
case """TEST: .*""" => println( "TEST" )
case """OXF.*""" => println("XXX")
case _ => println("NO MATCHING")
}
}
The idea is to use it like groovy switch case regex match. But I can't seem to get to to compile. Whats the right way to do it in scala?
You could either match on a precompiled regular expression (as in the first case below), or add an if
clause. Note that you typically don't want to recompile the same regular expression on each case evaluation, but rather have it on an object.
val list = List("Not a match", "TEST: yes", "OXFORD")
val testRegex = """TEST: .*""".r
list.foreach { x =>
x match {
case testRegex() => println( "TEST" )
case s if s.matches("""OXF.*""") => println("XXX")
case _ => println("NO MATCHING")
}
}
See more information here and some background here.
Starting Scala 2.13, it's possible to directly pattern match a String by unapplying a string interpolator:
// val examples = List("Not a match", "TEST: yes", "OXFORD")
examples.map {
case s"TEST: $x" => x
case s"OXF$x" => x
case _ => ""
}
// List[String] = List("", "yes", "ORD")

How to use a Map value in a match case statement in Scala

I have a map like this:
val mealIdsMap: Map[String, String] =
Map (
"breakfast" -> "omelet",
"lunch" -> "steak",
"dinner" -> "salad"
)
Then I try to use it in a match statement like this:
"omelet" match
{
case mealIdsMap("breakfast") => "Thank God"
}
And I get this error:
error: value mealIdsMap is not a case class constructor,
nor does it have an unapply/unapplySeq method
case mealIdsMap("breakfast") => "Thank God"
Anyone know how to use a map like this in a match/case statement?
Thanks alot for your help.
You should read what is the purpose of pattern matching from a tutorial, may be from this one (first non trivial example on google).
You have inverted the test:
mealIdsMap("breakfast") match {
case "omelet" => "Thank God"
case _ => "Don't forget a default"
}
And if you're not sure that the key is present (and even if you are, if you want to write idiomatic scala, you should prefer:
mealIdsMap.get("breakfast") match {
case Some("omelet") => "Thank God"
case _ => "Don't forget a default"
}
Where getreturns an option, avoiding you to try catch your code or to let it break silently.
Though, its still interesting to try to achieve such a behavior. have a look at this example:
case class InvMatcher (m:Map[String, String]){
def unapply(v:String):Option[String] = {
return m collectFirst {case (k, `v`) => k}
}
}
This class helps you to inverse-match a map.
usage:
val ma = InvMatcher (Map (
"breakfast" -> "omelet",
"lunch" -> "steak",
"dinner" -> "salad"
));
"steak" match {
case ma(s) => s match {
case "breakfast" => print("Thank God")
case "lunch" => print("whatever")
case _ => print("dont forget default")
}
case _ => print("dont forget default")
}
This is nearly as you wanted it though you need a second match-statement (which doesnt need a default case here...)