Clean up tags from a text I need to generate random text for tests. I do that using alphanumeric characters and some special characters as well.

For some special cases, I cannot generate the random text with tags.
I have the concept of a tag as text surrounded in curly braces (e.g. {name}).

I have written a simple code to clean up the tags (e.g. removing one of the curly braces) and I'd like some feedback if looks ok for Scala.

val allCharacters = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890!@#%*()_+-=[]{x}|?.;:`"

def removeTags(text: String): String = {

    // A tag is any text between {}, like `{name}`
    def containsTag(text: String): Boolean = {
        val noTagsRegex: Pattern = Pattern.compile("^((?!(\\{.*\\})).)*$")
        !noTagsRegex.matcher(text).matches()
    }

    var newText = text
    if (containsTag(newText)) {
        val randomPosition = Random.nextInt(allCharacters.length - 1)
        newText = text.replace("}", allCharacters(randomPosition).toString)
    }
    newText
}


Below an idea of results running this method:


Text without tags


input: "dlfkhfks sldfjhsldf qiweo 93450934 %&amp;%&amp;%&amp;^%"
output: "dlfkhfks sldfjhsldf qiweo 93450934 %&amp;%&amp;%&amp;^%" (exactly the same)

Text with 1 tag


input: "dlfkhfks s{ldf}jhsldf qiweo 93450934 %&amp;%&amp;%&amp;^%"
output: "dlfkhfks s4ldf}jhsldf qiweo 93450934 %&amp;%&amp;%&amp;^%" (the { was replaced)

Text with more than 1 tag


input: "dlfkhfks s{ldf}jhsldf {{@}} qiweo 93450934 %&amp;%&amp;%&amp;^%"
output: "dlfkhfks s4ldf}jhsldf 44@}} qiweo 93450934 %&amp;%&amp;%&amp;^%"



There is room for improvements, like not doing replace to all occurrences by the same character, but this is something for another moment.
Beautify your regex
Say No to Pattern.compile and instead of

 Pattern.compile(&quot;^((?!(\\{.*\\})).)*$&quot;)


use scalish way:
 &quot;^((?!(\\{.*\\})).)*$&quot;.r

Say No to \\ in regex and instead of

&quot;^((?!(\\{.*\\})).)*$&quot;


use &quot;&quot;&quot; way:
&quot;&quot;&quot;^((?!(\{.*\})).)*$&quot;&quot;&quot;

Return values without val/var
You can simply write your containsTag without val and result of last line will return from function - it will return your's matches:
def containsTag(text: String): Boolean = {
  ! &quot;&quot;&quot;^((?!(\{.*\})).)*$&quot;&quot;&quot;.r.matches(text)
}

Choose your destiny
Sometimes it is good to use if/else but more readable match/case:
containsTag(text) match {
  case true =&gt;
    val randomPosition = Random.nextInt(allCharacters.length - 1)
    text.replace(&quot;}&quot;, allCharacters(randomPosition).toString)
  case _ =&gt;
    text
}

Feel the power
Collect it all together:
  val allCharacters = &quot;qwertyuiopasdfghjklzxcvbnmQWERTY&quot; +
    &quot;UIOPASDFGHJKLZXCVBNM1234567890!@#%*()_+-=[]{x}|?.;:`&quot;

  def removeTags(text: String): String = {

    def containsTag(text: String): Boolean = {
      ! &quot;&quot;&quot;^((?!(\{.*\})).)*$&quot;&quot;&quot;.r.matches(text)
    }

    containsTag(text) match {
      case true =&gt;
        val randomPosition = Random.nextInt(allCharacters.length - 1)
        text.replace(&quot;}&quot;, allCharacters(randomPosition).toString)
      case _ =&gt;
        text
    }
  }

  val res = removeTags(&quot;my friendly {name} is $userNickname$&quot;)

For random chars
According to answer in Replacing characters in a String in Scala you can use construction without while loop but with map + lambda like in code below:
  val allCharacters = &quot;+=-?&quot;

  def removeTags(text: String): String = {

    def containsTag(text: String): Boolean = {
      ! &quot;&quot;&quot;^((?!(\{.*\})).)*$&quot;&quot;&quot;.r.matches(text)
      true
    }

    containsTag(text) match {
      case true =&gt;
        text.map(
          (c) =&gt; c match {
            case '{' =&gt;
              val randomPosition = Random.nextInt(allCharacters.length - 1)
              allCharacters(randomPosition)
            case c =&gt; c // for another char which is not == {
          }
        )
      case _ =&gt;
        text
    }
  }

Then it will be good to divide functionality and create new Char =&gt; Char transformer function from anonymous (c) =&gt; c match {..} which we lay in text.map(...):
//...
def replaceRandom(c: Char): Char = {
  c match {
    case '{' =&gt;
      val randomPosition = Random.nextInt(allCharacters.length - 1)
      allCharacters(randomPosition)
    case c =&gt; c
  }
}

containsTag(text) match {
  case true =&gt; text.map((c) =&gt; {replaceRandom(c)})
  case _ =&gt; text
}
//...

Simplify code in two parts.
Part 1: simplify match/case in replaceRandom(c: Char): Char :
def replaceRandom(c: Char): Char = {
  c match {
    case '{' =&gt;
      val randomPosition = Random.nextInt(allCharacters.length - 1)
      allCharacters(randomPosition)
    case c =&gt; c
  }
}

// remove unneeded &quot;{&quot; and &quot;}&quot;
def replaceRandom2(c: Char): Char = c match {
  case '{' =&gt;
    val randomPosition = Random.nextInt(allCharacters.length - 1)
    allCharacters(randomPosition) 
  case c =&gt; c
}

Then part 2: simplify map((c) =&gt; {replaceRandom(c)}):
containsTag(text) match {
  case true =&gt; text.map((c) =&gt; {replaceRandom(c)})
  case _ =&gt; text
}

//remove unneeded &quot;{&quot; and &quot;}&quot;
containsTag(text) match {
  case true =&gt; text.map((c) =&gt; replaceRandom(c))
  case _ =&gt; text
}

//remove unneeded &quot;(c)&quot; to wildcard &quot;_&quot;
containsTag(text) match {
  case true =&gt; text.map(replaceRandom(_))
  case _ =&gt; text
}

//remove unneeded wildcard &quot;_&quot;
containsTag(text) match {
  case true =&gt; text.map(replaceRandom)
  case _ =&gt; text
}

Feel the power again
So for your task i will chose that variant: all functions are divided, logic placed is simple if/else and now good readable (as you see now it good readable also with if/else as i wrote to you above). After, when logic will grow you can use instead if/else match/case.
  def removeTags(text: String): String = {

    def containsTag(text: String): Boolean = {
      ! &quot;&quot;&quot;^((?!(\{.*\})).)*$&quot;&quot;&quot;.r.matches(text)
      true
    }

    def replaceRandom(c: Char): Char = c match {
      case '{' =&gt;
        val randomPosition = Random.nextInt(allCharacters.length - 1)
        allCharacters(randomPosition)
      case c =&gt; c
    }

    if (containsTag(text))
      text.map(replaceRandom)
    else text

  }

  val allCharacters = &quot;+=-?&quot;
  val e = &quot;my fr{{{iend{{{ly {{name} is $userNickname$&quot;
  println(removeTags(e))


P.S. or place to think

Maybe your regex not working? Check it at: Regex101
Why you need to replace } symbol with another random from allCharacters  which can be also }?
The bad, the good and the !: maybe try to rewrite your !noTagsRegex to noTagsRegex? It can be more readable for another people.


Also i want to see additions to my review too, that code is too verbose, so welcome!