Add a marker character between duplicate pair in list

Viewed 71

I am working on an exercise that I need to figure out how to add designated marker char between two duplicate elements in a list.

input - a string
output - a list of string pairs

Two rules;

  1. if the input string has duplicate characters, a char x needs to be added between them. For ex; trees will become tr, ex, es
  2. if the duplicate char pair is xx, add a q between them. For ex; boxx becomes bo,xq, x

Both rules run together on the input, For example; if the input is HelloScalaxxxx the output should be List("He", "lx", "lo", "Sc", "al", "ax", "xq", "xq", "x")

I got the first rule working with following code and struggling to get the second rule satisfied.

 input.foldRight[List[Char]](Nil) {
      case (h, t) =>
        println(h :: t)
        if (t.nonEmpty) {
          (h, t.head) match {
            case ('x', 'x') => t ::: List(h, 'q')
            case _ => if (h == t.head) h :: 'x' :: t else h :: t
          }
        } else h :: t
    }
      .mkString("").grouped(2).toSeq

I think I am close, for the input HelloScalaxxxx it produces List("He", "lx", "lo", "Sc", "al", "ax", "xq", "xq", "xq"), but with an extra q in the last pair.

I don't want to use a regex-based solution. Looking for an idiomatic Scala version.

I tried searching for existing answers but no luck. Any help would be appreciated. Thank you.

2 Answers

I assume you want to apply the xx rule first...but you can decide.

"Trees & Scalaxxxx"
  .replaceAll("(x)(?=\\1)","$1q")
  .replaceAll("([^x])(?=\\1)","$1x")
  .grouped(2).toList
//res0: List[String] = List(Tr, ex, es, " &", " S", ca, la, xq, xq, xq, x)

And here's the non-regex offering.

"Trees & Scalaxxxx"
  .foldLeft(('_',"")){
    case (('x',acc),'x')           => ('x', s"${acc}qx")
    case ((p,acc),c) if c == p && 
                        p.isLetter => ( c , s"${acc}x$c")
    case ((_,acc),c)               => ( c , s"$acc$c")
  }._2.grouped(2).toList

Tail recursive solution

def processString(input: String): List[String] = {
  @scala.annotation.tailrec
  def inner(buffer: List[String], str: String): List[String] = {
    // recursion ending condition. Nothing left to process
    if (str.isEmpty) return buffer

    val c0 = str.head
    val c1 = if (str.isDefinedAt(1)) {
      str(1)
    } else {
      // recursion ending condition. Only head remains.
      return buffer :+ c0.toString
    }

    val (newBuffer, remainingString) =
    (c0, c1) match {
      case ('x', 'x') =>          (buffer :+ "xq", str.substring(1))
      case (_, _)  if c0 == c1 => (buffer :+ s"${c0}x", str.substring(1))
      case _ =>                   (buffer :+ s"$c0$c1", str.substring(2))
    }
    inner(newBuffer, remainingString)
  }
  // start here. Pass empty buffer and complete input string
  inner(List.empty, input)
}

println(processString("trees"))
println(processString("boxx"))
println(processString("HelloScalaxxxx"))
Related