Tuesday, January 26, 2010

String interpolation in Scala with Regex

Some new stuff I was waiting for has arrived on Scala. We can now have a limited form of string interpolation very easily:


def interpolate(text: String, vars: Map[String, String]) =
"""\$\{([^}]+)\}""".r.replaceAllIn(text, (_: scala.util.matching.Regex.Match) match {
case Regex.Groups(v) => vars.getOrElse(v, "")
})


Which can then be used like this:


scala> "The ${name} of the ${game}"
res0: java.lang.String = The ${name} of the ${game}

scala> Map("name" -> "X", "game" -> "Y")
res1: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map(name -> X, game -> Y)

scala> interpolate(res0, res1)
res2: String = The X of the Y

12 comments:

  1. What has arrived that makes that possible/easier?

    ReplyDelete
  2. I guess it's this commit: http://lampsvn.epfl.ch/trac/scala/changeset/20661

    ReplyDelete
  3. Which could, with an implicit, written as

    "The ${name} of the ${game}".fill("name" -> "X", "game" -> "Y")

    ReplyDelete
  4. or use expressions in {} in xml and convert the xml to string with the text method of xml

    ReplyDelete
  5. It's not as convenient, but you might use
    "The %s of the %s".format("X", "Y")

    ReplyDelete
  6. using the same thread comments (Sorry, cant find better place ) to another question (easy mode) . If i desires , for exemplo, increment all elements from a List, what is the best approach ?
    I can´t use the foreach, allright ? Cause lists are immutable, right ?
    list foreach ( e => e+1 ) do not increment each element by one.
    What should i do ?

    ReplyDelete
  7. Bruno, o melhor lugar para fazer tal pergunta é http://www.stackoverflow.com, em minha opinião.

    Em resposta à sua pergunta, "list map (e => e + 1)" retorna uma nova lista com todos os elementos incrementados em 1.

    ReplyDelete
  8. $ scala-2.8.0.r21356-b20100407020120/bin/scalac Foo.scala
    Foo.scala:20: error: missing parameter type for expanded function ((x0$1) => x0$1 match {
    case Regex.Groups((v @ _)) => vars.getOrElse(v, "")
    })
    """\$\{([^}]+)\}""".r.replaceAllIn(text, {
    ^
    one error found

    ReplyDelete
  9. This comment has been removed by a blog administrator.

    ReplyDelete
  10. Most curious. It used to work, and it _ought_ to work, but it obviously isn't. I fixed by making the match explicit.

    ReplyDelete
  11. You don't have to specify the type explicitly again. This is sufficient:

    def interpolate(text: String, vars: Map[String, String]) =
    """\$\{([^}]+)\}""".r.replaceAllIn(text, _ match {
    case Regex.Groups(v) => vars.getOrElse(v, "")
    })

    And IMHO the "_ match" makes indeed sense as opposed to the original version.

    ReplyDelete