Reputation: 42100
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?
Upvotes: 0
Views: 1055
Reputation: 38247
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.
Upvotes: 10
Reputation: 62855
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
Upvotes: 5
Reputation: 18869
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
Upvotes: 4