I'm new to Scala but I was told that "You are checking if "Toronto Raptor" == matchNY." for the following code snippet # https://issues.scala-lang.org/browse/SI-7210, and I really don't have any idea why "Totonto Raptor" is the only string chosen in the for loop to be matched with the regular expression, can someone explain this to me please ?
Thanks.
David
val matchNY = "^.*New.*$".r
val teams = List(
"Toronto Raptor",
"New York Nets",
"San Francisco 49ers",
"Dallas Mavericks"
)
for (team <- teams) {
team match {
case `matchNY` => println("Go New York.")
case _ => println("Boo")
}
}
Note-1: The usage of backticks is explained here # http://alvinalexander.com/scala/scala-unreachable-code-due-to-variable-pattern-message
I am assuming you meant
val matchNY = "^.*New.*$".r
and not ^.New.$, if you were expecting to match strings containing New.
In Scala, a block of case statements can be thought of as a sequence
of partial functions.
In your example,
case `matchNY` => // ...
Translates to something like:
case x if x == matchNY => // ..
So that will try to match the String "Toronto Raptor" with the Regexp object ^.*New.*$
using equality:
"Toronto Raptor" == ("^.*New.*$".r)
Which doesn't match because a String and a Regexp object are 2 different things.
The same goes for any of the other Strings in the list:
"New York Nets" != ("^.*New.*$".r)
Which doesn't match either. The way to use a regexp as a match in a case statement is:
case matchNY() => // .... Note the ()
Which, under the hood is (roughly) equivalant to something like
case x matchNY.unapplySeq(x).isDefined => // ...
Regexps in case statements are implemented as Extractor Objects with
an unapplySeq method. The last expression shows what the previous
translates into.
If matchNY had a capture such as:
val matchNY = "^.*New York\s(.*)$".r
Then you could use it to extract the captured match:
case matchNY(something) => // 'something' is a String variable
// with value "Nets"
Side Note
Your example could be condensed to
teams foreach {
case matchNY() => println("Go New York.")
case _ => println("Boo")
}
Yes, it is working properly. The magic behinds pattern matching is something called extractors.
If you dig through the ScalaDoc of Regex, you will see that it only defined unapplySeq, but not unapply.
That means if you want use Regex at Pattern Matching, you should do the following (note the parentheses after matchNY) :
val matchNY = "^.*New.*$".r
val teams = List(
"Toronto Raptor",
"New York Nets",
"San Francisco 49ers",
"Dallas Mavericks"
)
for (team <- teams) {
team match {
case matchNY() => println("Go New York.")
case _ => println("Boo")
}
}
Otherwise, you are simply checking if the elements in the list is == matchNY, which is not what you want anyway.
In your for loop you're literally checking if each item in the list of teams is equal to the regex matchNY and every item in the list is checked not just "Toronto Raptor". This is equivalent to your for loop:
for (team <- teams) {
if (team == matchNY) println("Go New York.")
else println("Boo")
}
Which breaks down to this:
if ("Toronto Raptor" == matchNY) println("Go New York.") else println("Boo")
if ("New York Nets" == matchNY) println("Go New York.") else println("Boo")
if ("San Francisco 49ers" == matchNY) println("Go New York.") else println("Boo")
if ("Dallas Mavericks" == matchNY) println("Go New York.") else println("Boo")
What I think your looking for is if something matches you're regex. You can do something like this:
for (team <- teams) {
matchNY.findFirstMatchIn(team) match {
case Some(teamMatch) => {
println("Go New York.")
println(teamMatch)
}
case _ => {
println("Boo")
println(team)
}
}
}
Which prints out:
Boo
Toronto Raptor
Go New York.
New York Nets
Boo
San Francisco 49ers
Boo
Dallas Mavericks
Related
Is there a way to rewrite this conditional as a pattern matching?
val oldEmail : Option[String]
val newEmail : Option[String]
if (newEmail.isDefined && (oldEmail.isEmpty || newEmail.get != oldEmail.get))
sendActivationEmail(newEmail.get)
else
()
my only failed attempt was this:
(newEmail, oldEmail) match {
case (Some(_), None) | (Some(ne), Some(oe)) if ne != oe =>
sendActivationEmail(newEmail.get)
case _ => ()
}
Edit: I should explicitly mention that my goal is a pattern matching with only two case clauses as shown above, for it's learning value
That logic does make for a rather complicated pattern. I'd be tempted to skip pattern matching.
newEmail.collect{
case ne if oldEmail.fold(true)(_ != ne) => sendActivationEmail(ne)
}
Update incorporating worthy input from #Alexey and #Cyrille Corpet and a suggestion from the IntelliJ IDE. (peer review ;-)
newEmail.foreach(ne => if (!oldEmail.contains(ne)) sendActivationEmail(ne))
You can make the pattern match work by having two different matches, but unless you're just calling the same method, there's some unwanted code duplication:
def f2(newEmail:Option[String], oldEmail:Option[String]) =
(newEmail, oldEmail) match {
case (Some(ne), None) =>
sendActivationEmail(ne)
case (Some(ne), Some(oe)) if ne != oe =>
sendActivationEmail(ne)
case _ =>
()
}
You can have multiple pattern matches as below,
test("given empty old email sends email to new email") {
val oldEmailOpt : Option[String] = None
val newEmailOpt : Option[String] = Some("this-is-my-beatifool email")
val result = newEmailOpt match {
case Some(newEmail) => oldEmailOpt match {
case Some(oldEmail) if !newEmail.equals(oldEmail) => "send email to new email"
case None => "send email to new email"
}
case None => "sending email to new email"
}
result shouldBe "send email to new email"
}
I have Array Data like this : [("Bob",5),("Andy",10),("Jim",7),...(x,y)].
How to do pattern matching in Scala? so they will match automatically based on Array Data that i have provided (instead of define "Case" one by one)
i mean dont like this, pseudocode :
val x = y.match {
case "Bob" => get and print Bob's Score
case "Andy" => get and print Andy's Score
..
}
but
val x = y.match {
case automatically defined by given Array => print each'score
}
Any Idea ? thanks in advance
If printing and storing results in an array is your main concern than the following will work well:
val ls = Array(("Bob",5),("Andy",10),("Jim",7))
ls.map({case (x,y) => println(y); y}) // print and store the score in an array
A bit confused about the question however if you just wish to print all the data in the array i would go about it doing this:
val list = Array(("Foo",3),("Tom",3))
list.foreach{
case (name,score) =>
println(s"$name scored $score")
}
//output:
//Foo scored 3
//Tom scored 3
Consider
val xs = Array( ("Bob",5),("Andy",10),("Jim",7) )
for ( (name,n) <- xs ) println(s"$name scores $n")
and also
xs.foreach { t => println(s"{t._1} scores ${t._2}") }
xs.foreach { t => println(t._1 + " scores " + t._2) }
xs.foreach(println)
A simple way to print the contents of xs,
println( xs.mkString(",") )
where mkString creates a string out of xs and separates each item by a comma.
Miscellany notes
To illustrate pattern matching on Scala Array, consider
val x = xs match {
case Array( t # ("Bob", _), _*) => println("xs starts with " + t._1)
case Array() => println("xs is empty")
case _ => println("xs does not start with Bob")
}
In the first case we extract the first tuple, and neglect the rest. In the first tuple we match against string "Bob" and neglect the second item. Moreover, we bind the first tuple to tag t, which is used in the printing where we refer to its first item.
The second case means every other case not covered.
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")
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")
My method definition looks as follows
def processLine(tokens: Array[String]) = tokens match { // ...
Suppose I wish to know whether the second string is blank
case "" == tokens(1) => println("empty")
Does not compile. How do I go about doing this?
If you want to pattern match on the array to determine whether the second element is the empty string, you can do the following:
def processLine(tokens: Array[String]) = tokens match {
case Array(_, "", _*) => "second is empty"
case _ => "default"
}
The _* binds to any number of elements including none. This is similar to the following match on Lists, which is probably better known:
def processLine(tokens: List[String]) = tokens match {
case _ :: "" :: _ => "second is empty"
case _ => "default"
}
What is extra cool is that you can use an alias for the stuff matched by _* with something like
val lines: List[String] = List("Alice Bob Carol", "Bob Carol", "Carol Diane Alice")
lines foreach { line =>
line split "\\s+" match {
case Array(userName, friends#_*) => { /* Process user and his friends */ }
}
}
Pattern matching may not be the right choice for your example. You can simply do:
if( tokens(1) == "" ) {
println("empty")
}
Pattern matching is more approriate for cases like:
for( t <- tokens ) t match {
case "" => println( "Empty" )
case s => println( "Value: " + s )
}
which print something for each token.
Edit: if you want to check if there exist any token which is an empty string, you can also try:
if( tokens.exists( _ == "" ) ) {
println("Found empty token")
}
case statement doesn't work like that. That should be:
case _ if "" == tokens(1) => println("empty")