Home > OS >  Scala: How to uppercase every other letter
Scala: How to uppercase every other letter

Time:05-22

I was asked this question in an interview. She wanted the answer in scala not java. My answer was java centric. Note that you can NOT use the character index to decide whether to uppercase and lowercase. Can you find a better way to make it scala centric? I can't think of any StringOps builtin functions that would help here. Focus on removing the need for var. I rewrote it using tail recursion (afterwards) but the answer was inefficient so I'm sticking with this java centric answer.

object Cap extends App {
  def _capitalizeEveryOtherLetter(str: String): String = {
    val s2: Array[Char] = str.toCharArray
    var capitalize: Boolean = true
    val arr: Array[Char] = (for (c <- s2)  yield  {
      if (!Character.isLetter(c)) {
        c
      } else if (capitalize) {
        capitalize = false
        val newChar = Character.toUpperCase(c)
        newChar
      } else {
        capitalize = true
        val newChar = Character.toLowerCase(c)
        newChar
      }
    })
    new String(arr)
  }
  val s1: String = "*7yTuu(i&^giuisKJSU&Y"
  println(s"${_capitalizeEveryOtherLetter(s1)}")
}

The expected output is

*7YtUu(I&^gIuIsKjSu&Y

Similar but not the same as How to convert every other character to upper case in a string since it relies on indexing which is not valid for this question.

CodePudding user response:

Since string manipulation is expensive I would just use a StringBuilder and a foldLeft

def capitalizeEveryOtherLetter(str: String): String = {
  val sb = new StringBuilder(str.size)
  
  str.foldLeft(false) {
    case (flag, char) =>
      val isLetter = char.isLetter
      if (isLetter && flag) {
        sb  = char.toUpper
        false
      } else {
        sb  = char.toLower
        isLetter || flag
      }
  }
  
  sb.result()
}

You can see the code running here.

CodePudding user response:

The scala way if you only had letters would be something like

input.grouped(2).map(_.capitalize).mkString("")

Skipping non-letter chars makes it somewhat more complicated: first figure out how each letter should be transformed, then replace them:

val letters = input.filter(_.isLetter).grouped(2).flatMap(_.capitalize)
input.map { c => if (c.isLetter) letters.next else c }

But this is probably too "clever" for an interview :) (well, depends where you are interviewing).

The "official" scala approach to "traverse/transform with state" is either (tail) recursion or (for simpler cases) foldLeft with an accumulator. And the trick to avoid both mutability and the inefficiency of appending things to strings or collections mentioned in the other answer is building a list, and reversing it in the end.

input.foldLeft[(Boolean, List[Char])](true -> Nil) { 
  case ((x, ac), c) if !c.isLetter => (x, c::ac)
  case ((true, ac), c) => (false, c.toUpper :: ac)
  case ((_, ac), c) => (true, c :: ac)
}._2.reverse.mkString("")

Since your question is labeled tail-recursion, here is a tail-recursive way (it's not really all that different from the foldLeft above):

   @tailrec
   def upSome(
       chars: List[Char], 
       state: Boolean = true, 
       result: List[Char]
   ): List[Char] = (state, chars) match { 
     case (_, Nil) => result.reverse
     case (_, c::tail) if !c.isLetter => upSome(tail, state, c::result)
     case (true, c::tail) => upSome(tail, false, c::result)
     case (_, c::tail) => upSome(tail, true, c::result)
   }

You would use it as upSome(input.toList).mkString("")

CodePudding user response:

Another (more functional?) one, that sidesteps the need to keep a "are we lower casing or uppercasing" status:

val xs  = "the quick brown fox jumps over the lazy dog";

val fs: Stream[Char =>  Char] = 
      List((c:Char) =>c.toUpper, (c:Char)=>c.toLower).toStream #::: fs

(xs zip fs).map{case (c, f) => f(c)}.mkString

It's possible I mis-interpreted the intent of the question though - this alternates upppercasing and lowercasing without regard to whether it's a letter or not. I leave it because it's an interesting alternate approach

  • Related