Better String formatting in Scala

StringScalaFormattingString Formatting

String Problem Overview


With too many arguments, String.format easily gets too confusing. Is there a more powerful way to format a String. Like so:

"This is #{number} string".format("number" -> 1)

Or is this not possible because of type issues (format would need to take a Map[String, Any], I assume; don’t know if this would make things worse).

Or is the better way doing it like this:

val number = 1
<plain>This is { number } string</plain> text

even though it pollutes the name space?

Edit:

While a simple pimping might do in many cases, I’m also looking for something going in the same direction as Python’s format() (See: http://docs.python.org/release/3.1.2/library/string.html#formatstrings)

String Solutions


Solution 1 - String

In Scala 2.10 you can use string interpolation.

val height = 1.9d
val name = "James"
println(f"$name%s is $height%2.2f meters tall")  // James is 1.90 meters tall

Solution 2 - String

Well, if your only problem is making the order of the parameters more flexible, this can be easily done:

scala> "%d %d" format (1, 2)
res0: String = 1 2

scala> "%2$d %1$d" format (1, 2)
res1: String = 2 1

And there's also regex replacement with the help of a map:

scala> val map = Map("number" -> 1)
map: scala.collection.immutable.Map[java.lang.String,Int] = Map((number,1))

scala> val getGroup = (_: scala.util.matching.Regex.Match) group 1
getGroup: (util.matching.Regex.Match) => String = <function1>

scala> val pf = getGroup andThen map.lift andThen (_ map (_.toString))
pf: (util.matching.Regex.Match) => Option[java.lang.String] = <function1>

scala> val pat = "#\\{([^}]*)\\}".r
pat: scala.util.matching.Regex = #\{([^}]*)\}

scala> pat replaceSomeIn ("This is #{number} string", pf)
res43: String = This is 1 string

Solution 3 - String

You can easily implement a richer formatting yourself (with the "enhance my library" approach):

scala> implicit def RichFormatter(string: String) = new {
     |   def richFormat(replacement: Map[String, Any]) =
     |     (string /: replacement) {(res, entry) => res.replaceAll("#\\{%s\\}".format(entry._1), entry._2.toString)}
     | }
RichFormatter: (string: String)java.lang.Object{def richFormat(replacement: Map[String,Any]): String}

scala> "This is #{number} string" richFormat Map("number" -> 1)
res43: String = This is 1 string

Or on more recent Scala versions since the original answer:

implicit class RichFormatter(string: String) {
  def richFormat(replacement: Map[String, Any]): String =
    replacement.foldLeft(string) { (res, entry) =>
      res.replaceAll("#\\{%s\\}".format(entry._1), entry._2.toString)
    }
}

Solution 4 - String

Maybe the Scala-Enhanced-Strings-Plugin can help you. Look here:

Scala-Enhanced-Strings-Plugin Documentation

Solution 5 - String

This the answer I came here looking for:

"This is %s string".format(1)

Solution 6 - String

If you're using 2.10 then go with built-in interpolation. Otherwise, if you don't care about extreme performance and are not afraid of functional one-liners, you can use a fold + several regexp scans:

val template = "Hello #{name}!"
val replacements = Map( "name" -> "Aldo" )
replacements.foldLeft(template)((s:String, x:(String,String)) => ( "#\\{" + x._1 + "\\}" ).r.replaceAllIn( s, x._2 ))

Solution 7 - String

You might also consider the use of a template engine for really complex and long strings. On top of my head I have Scalate which implements amongst others the Mustache template engine.

Might be overkill and performance loss for simple strings, but you seem to be in that area where they start becoming real templates.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDebilskiView Question on Stackoverflow
Solution 1 - StringAndrej HerichView Answer on Stackoverflow
Solution 2 - StringDaniel C. SobralView Answer on Stackoverflow
Solution 3 - StringVasil RemeniukView Answer on Stackoverflow
Solution 4 - StringRuediger KellerView Answer on Stackoverflow
Solution 5 - StringPriyank DesaiView Answer on Stackoverflow
Solution 6 - StringAldo BucchiView Answer on Stackoverflow
Solution 7 - StringDominik BucherView Answer on Stackoverflow