How to truncate a string after a word in scala

Given the following string...

"localhost:9000/one/two/three"

I want to truncate it after the word two and get

"localhost:9000/one/two"

I've implemented the methods truncateBefore and truncateAfter like this:

def truncateBefore(s: String, p: String) = {
  s.substring(s.indexOf(p) + p.length, s.length)
}

def truncateAfter(s: String, p: String) = {
  s.substring(0, s.indexOf(p) + p.length)
}

These methods work and return the expected results:

scala> truncateAfter("localhost:9000/one/two/three", "three")
res1: String = localhost:9000/one/two

scala> truncateBefore("localhost:9000/one/two/three", "three")
res2: String = /three

Is there a better way to do this in scala? Preferably with a regex?

Answers


Splitting after the first literal, without much regex-fu (pun intended).

scala> implicit class `split after`(val s: String) {
     | def splitAfter(p: String): (String, String) = {
     |   val r = (Regex quote p).r
     |   r findFirstMatchIn s map (m => (s.substring(0, m.end), m.after.toString)) getOrElse (s, "")
     | }}
defined class split$u0020after

scala> "abcfoodeffooghi" splitAfter "foo"
res2: (String, String) = (abcfoo,deffooghi)

scala> "abc*def" splitAfter "*"
res3: (String, String) = (abc*,def)

One option using regex:

val beforeAfter = "(^.*two)(.*)$".r

scala> val beforeAfter(after, before) = "localhost:9000/one/two/three"
after: String = localhost:9000/one/two
before: String = /three

Another option using split:

scala> "localhost:9000/one/two/three" split ("two")
res0: Array[java.lang.String] = Array(localhost:9000/one/, /three)

These are not super robust solutions in case you don't have a word two in the input, but you can handle it accordingly...

One more using regex in for comprehension:

scala> val beforeAfter = "(^.*two)(.*)$".r
beforeAfter: scala.util.matching.Regex = (^.*two)(.*)$

scala> (for {
     |   matches <- beforeAfter.findAllIn("localhost:9000/one/two/three").matchData
     |   tokens <- matches.subgroups
     |  } yield tokens).toList
res0: List[String] = List(localhost:9000/one/two, /three)

which is safe if no matches found:

scala> (for {
     |   match <- beforeAfter.findAllIn("localhost").matchData
     |   token <- match.subgroups
     |  } yield token).toList
res1: List[String] = List()

Obviously not efficient, but it works.

scala> val url = "localhost:9000/one/two/three"
url: String = localhost:9000/one/two/three

scala> url.reverse.dropWhile(_ != '/').reverse
res0: String = localhost:9000/one/two/

With an index:

scala> url.drop(url.reverse.indexOf('/'))
res1: String = host:9000/one/two/three

OK, thanks to everybody... and here is my solution:

package object typeExtensions {

  implicit class StringExtensions(val string: String) extends AnyVal {

    def truncateAfter(pattern: String) = beforeAfter(pattern).map(_._1).getOrElse(string)
    def truncateBefore(pattern: String) = beforeAfter(pattern).map(_._2).getOrElse(string)

    private def beforeAfter(pattern: String) = {
      if (string.contains(pattern)) {
        val beforeAfter = ("(^.*" + Pattern.quote(pattern) + ")(.*)$").r
        val beforeAfter(before, after) = string
        Some(before, after)
      } else None
    }
  }
}

Any suggestion to improve the code above is more than welcome ;-)


Need Your Help

Find values in list which sum to a given value

python algorithm python-2.7

I'm trying to code up something simple and pythonic to identify combinations of values from a list which sum to a defined value, within some tolerance.

How do I format a string?

c# string date

I have a string "5-13-2013"

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.