crowgers
crowgers

Reputation: 274

Scala - Remove all elements in a list/map of strings from a single String

Working on an internal website where the URL contains the source reference from other systems. This is a business requirement and cannot be changed. i.e. "http://localhost:9000/source.address.com/7808/project/repo" "http://localhost:9000/build.address.com/17808/project/repo"

I need to remove these strings from the "project/repo" string/variables using a trait so this can be used natively from multiple services. I also want to be able to add more sources to this list (which already exists) and not modify the method.

"def normalizePath" is the method accessed by services, 2 non-ideal but reasonable attempts so far. Getting stuck on a on using foldLeft which I woudl like some help with or an simpler way of doing the described. Code Samples below.

1st attempt using an if-else (not ideal as need to add more if/else statements down the line and less readable than pattern match)

trait NormalizePath {
    def normalizePath(path: String): String = {
        if (path.startsWith("build.address.com/17808")) {
            path.substring("build.address.com/17808".length, path.length)
        } else {
            path
        }
    }
}

and 2nd attempt (not ideal as likely more patterns will get added and it generates more bytecode than if/else)

trait NormalizePath {
    val pattern = "build.address.com/17808/"
    val pattern2 = "source.address.com/7808/"
    def normalizePath(path: String) = path match {
        case s if s.startsWith(pattern) => s.substring(pattern.length, s.length)
        case s if s.startsWith(pattern2) => s.substring(pattern2.length, s.length)
        case _ => path
    }
}

Last attempt is to use an address list(already exists elsewhere but defined here as MWE) to remove occurrences from the path string and it doesn't work:

trait NormalizePath {
    val replacements = (
        "build.address.com/17808",
        "source.address.com/7808/")

    private def remove(path: String, string: String) = {
        path-string
    }

    def normalizePath(path: String): String = {
        replacements.foldLeft(path)(remove)
    }
}   

Appreciate any help on this!

Upvotes: 1

Views: 1373

Answers (3)

user3097405
user3097405

Reputation: 823

If you are just stripping out those strings:

val replacements = Seq(
  "build.address.com/17808",
  "source.address.com/7808/")


replacements.foldLeft("http://localhost:9000/source.address.com/7808/project/repo"){
  case(path, toReplace) => path.replaceAll(toReplace, "")
}
// http://localhost:9000/project/repo

If you are replacing those string by something else:

val replacementsMap = Seq(
  "build.address.com/17808" -> "one",
  "source.address.com/7808/" -> "two/")


replacementsMap.foldLeft("http://localhost:9000/source.address.com/7808/project/repo"){
  case(path, (toReplace, replacement)) => path.replaceAll(toReplace, replacement)
}
// http://localhost:9000/two/project/repo

The replacements collection can come from elsewhere in the code and will not need to be redeployed.

// method replacing by empty string
def normalizePath(path: String) = {
  replacements.foldLeft(path){
    case(startingPoint, toReplace) => startingPoint.replaceAll(toReplace, "")
  }
}

normalizePath("foobar/build.address.com/17808/project/repo")
// foobar/project/repo

normalizePath("whateverPath")
// whateverPath

normalizePath("build.address.com/17808build.address.com/17808/project/repo")
// /project/repo

Upvotes: 2

James Whiteley
James Whiteley

Reputation: 3474

There are a million and one ways to extract /project/repo from a String in Scala. Here are a few I came up with:


val list = List("build.address.com/17808", "source.address.com/7808") //etc
def normalizePath(path: String) = {
  path.stripPrefix(list.find(x => path.contains(x)).getOrElse(""))
}

Output:

scala> normalizePath("build.address.com/17808/project/repo")
res0: String = /project/repo

val list = List("build.address.com/17808", "source.address.com/7808") //etc
def normalizePath(path: String) = {
  list.map(x => if (path.contains(x)) {
    path.takeRight(path.length - x.length)
  }).filter(y => y != ()).head
}

Output:

scala> normalizePath("build.address.com/17808/project/repo")
res0: Any = /project/repo

val list = List("build.address.com/17808", "source.address.com/7808") //etc
def normalizePath(path: String) = {
  list.foldLeft(path)((a, b) => a.replace(b, ""))
}

Output:

scala> normalizePath("build.address.com/17808/project/repo")
res0: String = /project/repo

Depends how complicated you want your code to look (or how silly you want to be), really. Note that the second example has return type Any, which might not be ideal for your scenario. Also, these examples aren't meant to be able to just take the String out of the middle of your path... they can be fairly easily modified if you want to do that though. Let me know if you want me to add some examples just stripping things like build.address.com/17808 out of a String - I'd be happy to do so.

Upvotes: 1

Antot
Antot

Reputation: 3964

A very simple replacement could be made as follows:

val replacements = Seq(
  "build.address.com/17808",
  "source.address.com/7808/")

def normalizePath(path: String): String = {
  replacements.find(path.startsWith(_)) // find the first occurrence
              .map(prefix => path.substring(prefix.length)) // remove the prefix
              .getOrElse(path) // if not found, return the original string
}

Since the expected replacements are very similar, have you tried to generalize them and use regex matching?

Upvotes: 1

Related Questions