I'm still learning the basics and I have a question.
I have a function
def reverse(s: String): String = {
s.reverse
}
Now I have a List[String] and I want to reverse each String element.
I've tried foreach, but it seems to return Unit, not String.
So, I want a List[String] with the same elements, but the strings reversed.
List(abcd, efgh) becomes List(dcba, hgfe).
What I have now:
def reverse(ls : List[String]):List[String] = {
List(ls.foreach (reverse))
}
Use map method:
List("abcd", "efgh").map(s => reverse(s))
Or simply:
List("abcd", "efgh").map(reverse)
Unlike foreach which is here for side effects (like printing things out) map does returns result.
try this,
List("abcd", "efgh").reverse
Related
I am starting to learn Scala and want to use regular expressions to match a character from a string so I can populate a mutable map of characters and their value (String values, numbers etc) and then print the result.
I have looked at several answers on SO and gone over the Scala Docs but can't seem to get this right. I have a short Lexer class that currently looks like this:
class Lexer {
private val tokens: mutable.Map[String, Any] = collection.mutable.Map()
private def checkCharacter(char: Character): Unit = {
val Operator = "[-+*/^%=()]".r
val Digit = "[\\d]".r
val Other = "[^\\d][^-+*/^%=()]".r
char.toString match {
case Operator(c) => tokens(c) = "Operator"
case Digit(c) => tokens(c) = Integer.parseInt(c)
case Other(c) => tokens(c) = "Other" // Temp value, write function for this
}
}
def lex(input: String): Unit = {
val inputArray = input.toArray
for (s <- inputArray)
checkCharacter(s)
for((key, value) <- tokens)
println(key + ": " + value)
}
}
I'm pretty confused by the sort of strange method syntax, Operator(c), that I have seen being used to handle the value to match and am also unsure if this is the correct way to use regex in Scala. I think what I want this code to do is clear, I'd really appreciate some help understanding this. If more info is needed I will supply what I can
This official doc has lot's of examples: https://www.scala-lang.org/api/2.12.1/scala/util/matching/Regex.html. What might be confusing is the type of the regular expression and its use in pattern matching...
You can construct a regex from any string by using .r:
scala> val regex = "(something)".r
regex: scala.util.matching.Regex = (something)
Your regex becomes an object that has a few useful methods to be able to find matching groups like findAllIn.
In Scala it's idiomatic to use pattern matching for safe extraction of values, thus Regex class also has unapplySeq method to support pattern matching. This makes it an extractor object. You can use it directly (not common):
scala> regex.unapplySeq("something")
res1: Option[List[String]] = Some(List(something))
or you can let Scala compiler call it for you when you do pattern matching:
scala> "something" match {
| case regex(x) => x
| case _ => ???
| }
res2: String = something
You might ask why exactly this return type on unapply/unapplySeq. The doc explains it very well:
The return type of an unapply should be chosen as follows:
If it is just a test, return a Boolean. For instance case even().
If it returns a single sub-value of type T, return an Option[T].
If you want to return several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)].
Sometimes, the number of values to extract isn’t fixed and we would
like to return an arbitrary number of values, depending on the input.
For this use case, you can define extractors with an unapplySeq method
which returns an Option[Seq[T]]. Common examples of these patterns
include deconstructing a List using case List(x, y, z) => and
decomposing a String using a regular expression Regex, such as case
r(name, remainingFields # _*) =>
In short your regex might match one or more groups, thus you need to return a list/seq. It has to be wrapped in an Option to comply with extractor contract.
The way you are using regex is correct, I would just map your function over the input array to avoid creating mutable maps. Perhaps something like this:
class Lexer {
private def getCharacterType(char: Character): Any = {
val Operator = "([-+*/^%=()])".r
val Digit = "([\\d])".r
//val Other = "[^\\d][^-+*/^%=()]".r
char.toString match {
case Operator(c) => "Operator"
case Digit(c) => Integer.parseInt(c)
case _ => "Other" // Temp value, write function for this
}
}
def lex(input: String): Unit = {
val inputArray = input.toArray
val tokens = inputArray.map(x => x -> getCharacterType(x))
for((key, value) <- tokens)
println(key + ": " + value)
}
}
scala> val l = new Lexer()
l: Lexer = Lexer#60f662bd
scala> l.lex("a-1")
a: Other
-: Operator
1: 1
I am wondering without creating a function, how can I filter out numbers from a list with both numbers and strings:
val a = sc.parallelize(List(“cat”,“horse”,4.0,3.5,2,“dog”))
I believe my question indeed is looking for how to use regex in Scala to find out matched pattern
----Updated on 20180302 11pm EST:
Thanks to #Nyavro which is the closest answer, I slightly modified as below:
val doubles = a.collect {
case v: Double => v
case v: Int => v
}
Now I get:
res10: Array[Double] = Array(4.0, 3.5, 2.0)
Just be curious, can types be mixed in a collect result in Scala?
Thanks to all replies.
Use collect:
val doubles = a.collect {
case v: Double => v
}
To filter for elements of type Int and Double, and to retain their respective types, you might try this.
a.flatMap {
case v: Int => Some(v)
case v: Double => Some(v)
case _ => None
}
//res0: List[AnyVal] = List(4.0, 3.5, 2)
To help understand why this is a really bad idea, read this question, and its answers.
You can use isInstanceOf to check whether an element of your list is a string.
val l = List("cat","horse",4.0,3.5,2,"dog")
l.filter(_.isInstanceOf[String])
>> List[Any] = List(cat, horse, dog)
Regex is (largely) irrelevant here because you do not have strings, you have a List[Any] that you're turning into an RDD[Any]. (The RDD is largely irrelevant here, too, except RDDs have no filterNot and Lists do--I can't tell if you want to keep the strings or drop the strings.)
Note also that filter takes a function as an argument--having some function here is inescapable, even if it's anonymous, as it is in my example.
I have an inkling, though, that I've given an answer to the opposite of what you're asking, and you have an RDD[String] that you want to convert to RDD[Double], throwing away the strings that don't convert. In that case, I would try to convert the strings to doubles, wrapping that in a Try and check for success, using the result to filter:
def isDouble(s: String) = Try(s.toDouble).isSuccess
I have a list in Groovy which contains the names in the below format:
My_name_is_Jack
My_name_is_Rock
My_name_is_Sunn
How can I trim the list and get only the last part of it; i.e. Names - Jack, Rock and Sunn. (Please note that the names are only 4 characters long)
Here you go with either one of the approach.
You can use sustring with lastIndexOf
or replace method to remove My_name_is_ with empty string
Script (using the first approach):
def list = ['My_name_is_Jack', 'My_name_is_Rock', 'My_name_is_Sunn']
//Closure to get the name
def getName = { s -> s.substring(s.lastIndexOf('_')+1, s.size()) }
println list.collect{getName it}
If you want to use replace, then use below closure.
def getName = { s -> s.replace('My_name_is_','') }
You can quickly try it online demo
Or
def list = ['My_name_is_Jack', 'My_name_is_Rock', 'My_name_is_Sunn']
println list*.split('_')*.getAt(-1)
You can either remove the common prefix:
def names = [ "My_name_is_Jack", "My_name_is_Rock", "My_name_is_Sunn", ]
assert ['Jack', 'Rock', 'Sunn'] == names*.replaceFirst('My_name_is_','')
or since you are actually interrested in the last four chars, you can also take those:
assert ['Jack', 'Rock', 'Sunn'] == names*.getAt(-4..-1)
How can you get the submap of map with a string being a pattern ? Example, I have this map :
def map = [val1:ATOPKLPP835, val2: ATOPKLPP847, val3:ATOPKLPP739, val4:YYHSTYSTX439, val5:UUSTETSFEE34]
The first three values are identical until the ninth character. I would like to get a submap only with the string "ATOPKLPP". How can I do ?
Have a look at this:
def map = [val1:'ATOPKLPP835', val2: 'ATOPKLPP847', val3:'ATOPKLPP739', val4:'YYHSTYSTX439', val5:'UUSTETSFEE34']
def found = map.findAll { it.value.startsWith('ATOPKLPP')}
assert found == [val1:'ATOPKLPP835', val2:'ATOPKLPP847', val3:'ATOPKLPP739']
You can define whatever criterion in closure passed to findAll.
Currently I use this to pick the first element of a list:
def Get_Read_Key =
{
logger.entering (TAG, "Get_Read_Key")
val Retval = if (Read_Key_Available)
{
val Retval = Keystrokes.head
Keystrokes = Keystrokes.tail
Retval
}
else
{
calculator.ui.IKey.No_Key
} // if
logger.exiting (TAG, "Get_Read_Key", Retval)
Retval
} // Get_Read_Key
def Read_Key_Available = Keystrokes.size > 0
But it looks all kind of clumsy — especially the double ´Retval´. Is there a better way of doing this? Or is it just the price to pay for using an immutable list?
Background: The routine is used on a Unit Test Mock class – return types are set.
The following code will get you the first element of Keystrokes list if it's not empty and calculator.ui.IKey.No_Key otherwise:
Keystrokes.headOption.getOrElse( calculator.ui.IKey.No_Key )
P.S. Reassigning Keystrokes to a tail is a definite sign of a bad design. Instead you should use the mentioned by themel already existing iteration capabilities of a list in your algorithm. Most probably using methods such as map or foreach will solve your problem.
P.P.S. You've violated several Scala naming conventions:
variable, value, method and function names begin with lowercase
camelCase is used to delimit words instead of underscore. In fact using underscore for these purposes is greatly discouraged due to Scala having a special treating of that specific character
You're implementing an Iterator on a List, that's already in the standard library.
val it = Keystrokes.iterator
def Read_Key_Available = it.hasNext
def Get_Read_Key = if(it.hasNext) it.next() else calculator.ui.IKey.No_Key
You can use pattern matching:
Keystrokes match {
case h::t =>
KeyStrokes = t
h
case _ =>
calculator.ui.IKey.No_key
}