How to do escaping idiomatically in Scala - scala

Suppose I am writing a simple function to escape XML string.
val map = Map('&' -> "&", '<' -> "<", '>' -> ">") // ... and more
def escape(str: String): String = {
val w = new java.io.StringWriter()
for(c <- str) if (map.contains(c)) w.write(map(c)) else w.write(c);
w.toString
}
It does not look idiomatically in Scala and besides, I do not know how to deal with the map, which maps characters to escaping strings. How would you suggest me fix it?

Real life solution
Just use scala.xml.Utility.escape:
scala> scala.xml.Utility.escape(foo)
res7: String = &foo<bar>hello
If you're still interested in doing it yourself:
First of all, there's no need to repeat the & and ; parts in the escapes map:
scala> val Escapes = Map('&' -> "amp", '<' -> "lt", '>' -> "gt")
(it's simpler and faster to have them in tho... but since you seem to have asked this question for learning purposes...)
scala> def escapeChar(c: Char) = Escapes.get(c).map { x => s"&$x;" }
escapeChar: (c: Char)Option[String]
scala> def escapeStr(s: String) = s.flatMap { c => escapeChar(c).getOrElse(c.toString) }
escapeStr: (s: String)String
scala> escapeStr("&foo<bar>hello")
res9: String = &foo<bar>hello
...you could also just inline the escapeChar(c: Char) function, but I think it's more readable this way.
In case you're interested: this works by treating the string as a sequence of chars; you flatMap over it, which allows you to map each char into more than one char (a String); flatMap then joins all the emitted strings into a single string. Characters that don't need escaping are trivially mapped to one-char strings.

Just for a sake of completeness -- basically the same solution, but with function method in place of the map:
def escape(char: Char) = char match {
case '&' => "&"
case '<' => "<"
case '>' => ">"
case noEscaping => noEscaping.toString
}
val str = "hit & run"
str.flatMap(escape)
// hit & run

How about this:
scala> val map = Map[Char, Seq[Char]]('&' -> "&",
'<' -> "<",
'>' -> ">").withDefault(x => Seq(x))
map: scala.collection.immutable.Map[Char,Seq[Char]] = Map(& -> &, < -> <, > -> >)
scala> "&foo<bar>hello".flatMap(map)
res2: String = &foo<bar>hello
OR
scala> val map = Map('&' -> "&", '<' -> "<", '>' -> ">").withDefault(identity)
scala> "&foo<bar>hello".map(map).mkString
res3: String = &foo<bar>hello

Related

Inverting case in Scala

for (i <- marker to cursor - 1 ){
if (buffer.charAt(i).isUpper){
buffer.charAt(i).toString.toLowerCase
} else if (buffer.charAt(i).isLower) {
buffer.charAt(i).toString.toUpperCase
}
}
I've tried multiple methods to achieve but can't figure a solution and this is where I'm at. While trying other methods I used slice but couldn't get it to return a Bool for an if statement (Converted to a string but isUpper doesn't work on strings). Currently this does nothing to the strings, for context marker/cursor just highlight a selection on a sentence to invert.
Here is a one liner:
val s = "mixedUpperLower"
s.toUpperCase.zip (s).map {case (a, b) => if (a == b) a.toLower else a}.mkString ("")
res3: String = MIXEDuPPERlOWER
Maybe a short method is better readable:
scala> def invertCase (c: Char) : Char = if (c.isLower) c.toUpper else c.toLower
invertCase: (c: Char)Char
scala> s.map (invertCase)
res4: String = MIXEDuPPERlOWER
"aBcDef".map(x => if(x.isLower) x.toUpper else x.toLower)
prints
AbCdEF

Scala- creating a map from string

Hi have a string and the if format of the string is mentioned below:
val str = "{a=10, b=20, c=30}"
All the parameters inside this string is unique and separated by comma and space. Also This string always starts with '{' and ends with '}'. I want to create a Map out of this string something like below:
val values = Map("a" -> 10, "b" -> 20, "c" -> 30)
What is the most efficient way I can achieve this?
scala> val str = "{a=10, b=20, c=30}"
str: String = {a=10, b=20, c=30}
scala> val P = """.*(\w+)=(\d+).*""".r
P: scala.util.matching.Regex = .*(\w+)=(\d+).*
scala> str.split(',').map{ case P(k, v) => (k, v.toInt) }.toMap
res2: scala.collection.immutable.Map[String,Int] = Map(a -> 10, b -> 20, c -> 30)
Use regex can simply achieve this:
"(\\w+)=(\\w+)".r.findAllIn("{a=10, b=20, c=30}").matchData.map(i => {
(i.group(1), i.group(2))
}).toMap
The function you want to write is pretty easy:
def convert(str : String) : Map[String, String] = {
str.drop(1).dropRight(1).split(", ").map(_.split("=")).map(arr => arr(0)->arr(1)).toMap
}
with drop and dropRight, you remove the brackets. Then you split the String with the expression ,, which results in multiple Strings.
Than you split each of this strings, which results in arrays with two elements. Those are used to create a map.
I would do it likes this (I think regex is not needed here):
val str = "{a=10, b=20, c=30}"
val values: Map[String, Int] = str.drop(1).dropRight(1) // drop braces
.split(",") // split into key-value pairs
.map { pair =>
val Array(k, v) = pair.split("=") // split key-value pair and parse to Int
(k.trim -> v.toInt)
}.toMap

Scala: Print separators when using output stream

When we need an array of strings to be concatenated, we can use mkString method:
val concatenatedString = listOfString.mkString
However, when we have a very long list of string, getting concatenated string may not be a good choice. In this case, It would be more appropriated to print out to an output stream directly, Writing it to output stream is simple:
listOfString.foreach(outstream.write _)
However, I don't know a neat way to append separators. One thing I tried is looping with an index:
var i = 0
for(str <- listOfString) {
if(i != 0) outstream.write ", "
outstream.write str
i += 1
}
This works, but it is too wordy. Although I can make a function encapsules the code above, I want to know whether Scala API already has a function do the same thing or not.
Thank you.
Here is a function that do what you want in a bit more elegant way:
def commaSeparated(list: List[String]): Unit = list match {
case List() =>
case List(a) => print(a)
case h::t => print(h + ", ")
commaSeparated(t)
}
The recursion avoids mutable variables.
To make it even more functional style, you can pass in the function that you want to use on each item, that is:
def commaSeparated(list: List[String], func: String=>Unit): Unit = list match {
case List() =>
case List(a) => func(a)
case h::t => func(h + ", ")
commaSeparated(t, func)
}
And then call it by:
commaSeparated(mylist, oustream.write _)
I believe what you want is the overloaded definitions of mkString.
Definitions of mkString:
scala> val strList = List("hello", "world", "this", "is", "bob")
strList: List[String] = List(hello, world, this, is, bob)
def mkString: String
scala> strList.mkString
res0: String = helloworldthisisbob
def mkString(sep: String): String
scala> strList.mkString(", ")
res1: String = hello, world, this, is, bob
def mkString(start: String, sep: String, end: String): String
scala> strList.mkString("START", ", ", "END")
res2: String = STARThello, world, this, is, bobEND
EDIT
How about this?
scala> strList.view.map(_ + ", ").foreach(print) // or .iterator.map
hello, world, this, is, bob,
Not good for parallelized code, but otherwise:
val it = listOfString.iterator
it.foreach{x => print(x); if (it.hasNext) print(' ')}
Here's another approach which avoids the var
listOfString.zipWithIndex.foreach{ case (s, i) =>
if (i != 0) outstream write ","
outstream write s }
Self Answer:
I wrote a function encapsulates the code in the original question:
implicit def withSeparator[S >: String](seq: Seq[S]) = new {
def withSeparator(write: S => Any, sep: String = ",") = {
var i = 0
for (str <- seq) {
if (i != 0) write(sep)
write(str)
i += 1
}
seq
}
}
You can use it like this:
listOfString.withSeparator(print _)
The separator can also be assigned:
listOfString.withSeparator(print _, ",\n")
Thank you for everyone answered me. What I wanted to use is a concise and not too slow representation. The implicit function withSeparator looks like the thing I wanted. So I accept my own answer for this question. Thank you again.

equivalent of pythons repr() in scala

Is it there an equivalent of Pythons repr function in scala?
Ie a function which you can give any scala object an it will produce a string representation of the object which is valid scala code.
eg:
val l = List(Map(1 -> "a"))
print(repr(l))
Would produce
List(Map(1 -> "a"))
There is mostly only the toString method on every object. (Inherited from Java.) This may or may not result in a parseable representation. In most generic cases it probably won’t; there is no real convention for this as there is in Python but some of the collection classes at least try to. (As long as they are not infinite.)
The point where it breaks down is of course already reached when Strings are involved
"some string".toString == "some string"
however, for a proper representation, one would need
repr("some string") == "\"some string\""
As far as I know there is no such thing in Scala. Some of the serialisation libraries might be of some help for this, though.
Based on the logic at Java equivalent of Python repr()?, I wrote this little function:
object Util {
def repr(s: String): String = {
if (s == null) "null"
else s.toList.map {
case '\0' => "\\0"
case '\t' => "\\t"
case '\n' => "\\n"
case '\r' => "\\r"
case '\"' => "\\\""
case '\\' => "\\\\"
case ch if (' ' <= ch && ch <= '\u007e') => ch.toString
case ch => {
val hex = Integer.toHexString(ch.toInt)
"\\u%s%s".format("0" * (4 - hex.length), hex)
}
}.mkString("\"", "", "\"")
}
}
I've tried it with a few values and it seems to work, though I'm pretty sure sticking in a Unicode character above U+FFFF would cause problems.
If you deal with case classes, you can mix in the following trait StringMaker, so that calling toString on such case classes will work even if their arguments are strings:
trait StringMaker {
override def toString = {
this.getClass.getName + "(" +
this.getClass.getDeclaredFields.map{
field =>
field.setAccessible(true)
val name = field.getName
val value = field.get(this)
value match {
case s: String => "\"" + value + "\"" //Or Util.repr(value) see the other answer
case _ => value.toString
}
}
.reduceLeft{_+", "+_} +
")"
}
}
trait Expression
case class EString(value: String, i: Int) extends Expression with StringMaker
case class EStringBad(value: String, i: Int) extends Expression //w/o StringMaker
val c_good = EString("641", 151)
val c_bad = EStringBad("641", 151)
will result in:
c_good: EString = EString("641", 151)
c_bad: EStringBad = EStringBad(641,151)
So you can parse back the firsst expression, but not the first one.
No, there is no such feature in Scala.

scala build up string from iterating over map

if i have a map and want to build up a string from iterating over it, is there a way to have the final string be a result of an expression instead of defining a variable and modifying that inside a loop?
instead of this
val myMap = Map("1" -> "2", "3"->"4")
var s = ""
myMap foreach s += ...
i'd rather it be
var s = myMap something ...
I'd just map and mkString. For example:
val s = (
Map("1" -> "2", "3"->"4")
map { case (key, value) => "Key: %s\nValue: %s" format (key, value) }
mkString ("", "\n", "\n")
)
As for Daniel's answer, but with a couple of optimisations and my own formatting preferences:
val myMap = Map("1" -> "2", "3"->"4")
val s = myMap.view map {
case (key, value) => "Key: " + key + "\nValue: " + value
} mkString ("", "\n", "\n")
The optimisations:
By first creating a view of the map, I avoid creating an intermediate collection
On profiling, direct String concatenation is faster than String.format
You can do this with a fold:
scala> myMap.foldLeft("") { (s: String, pair: (String, String)) =>
| s + pair._1 + pair._2
| }
res0: java.lang.String = 1234
I'm fairly new to Scala, but you can try reduceLeft. It goes accumulating a partial value (the string being joined with every element). For example, if you want the keys (or the values) joined in a string, just do:
val s = myMap.keys.reduceLeft( (e, s) => e + s)
This results in "13"
This works also fine if you don't bother about your own formatting:
scala> Map("1" -> "2", "3"->"4").mkString(", ")
res6: String = 1 -> 2, 3 -> 4