3
\$\begingroup\$

I have a string "abbbbccdddd" and the function should return "a1b4c2d4".

This is what I have written in Scala.

Iterative-version

def compress(str: String) = {
  val chars = List(str).flatten.map(_.toString) ++ List(null)
  var result: String = ""

  var lookBack: String = chars.head
  var occurance: Int = 0

  chars.foreach { c =>
    if (c != lookBack) {
      result = result + lookBack.toString + occurance
      occurance = 0
    }

    occurance = occurance + 1
    lookBack = c
  }

  result
}

Recursive-version

def compress(str: String): String = {
  def compressHandler(str: String, lookBack: String, occurance: Int, result: String): String = {
    if(str.isEmpty) {
      result
    } else if(str.head.toString == lookBack) {
      compressHandler(str.drop(1), str.head.toString, occurance + 1, result)
    } else {
      compressHandler(str, str.head.toString, 0, result + lookBack + occurance)
    }
  }

  compressHandler(str + "0", str.head.toString, 0, "")
}

Scala — being a functional language should have much better solutions!

How to improve second (by somehow using map/reduce/fold) and how to do the first following concept of immutability (purely functional)?

\$\endgroup\$
2
  • \$\begingroup\$ You can use mutable vars or collections. It is not tabu. The most important things are simplicity and sufficient speed. For speed you should not use head, tail and reverse for String in this task. For simplisity you can use simple counter var and index. In some tasks you should prefer FP, in some -- iterative, in some -- special functions. I advice you to see Scala Days with Martin Odersky for 2013 оr 2014 year \$\endgroup\$ Commented Jul 26, 2020 at 14:21
  • \$\begingroup\$ Scala days: Scala with style youtu.be/kkTFx3-duc8 \$\endgroup\$ Commented Jul 26, 2020 at 14:27

2 Answers 2

3
\$\begingroup\$

Here's my take using foldLeft:

def compress(s: String) = {
    val a : List[(Char,Int)] = List()
    s.toCharArray.foldLeft(a)((acc, elem) => acc match {
        case Nil => (elem, 1) :: Nil
        case (a, b) :: tail =>
            if (a == elem) (elem, b + 1) :: tail else (elem, 1) :: acc
    }).reverse
    .map{ case (a, b) => a.toString + b }
    .mkString("")
}

Note: I have assumed that order matters. That is, aabbbaa will reduce to a2b3a2

\$\endgroup\$
3
  • \$\begingroup\$ Cool stuff! However we can desugar this and get rid of ._1 and ._2 but it seems perfect to me. \$\endgroup\$ Commented May 8, 2020 at 9:38
  • \$\begingroup\$ changed to get rid of ._1 and ._2 \$\endgroup\$
    – Luqman
    Commented May 8, 2020 at 9:44
  • 2
    \$\begingroup\$ Welcome to code review. This answer is an alternate solution which is fine on stackoverflow.com but is not a good answer on code review. A good answer may not contain any code at all, but it must contain at least one meaningful observation about the code posted in the the question. Code can be used as examples to support the observations about the posters code. \$\endgroup\$
    – pacmaninbw
    Commented May 8, 2020 at 13:53
1
\$\begingroup\$

I would suggest omitting the allocation of extra space. Try to start with:

object Solution:
  def compress(str: String): String =
    val (sb, _) = 
      Vector.range(0, str.size + 1).foldLeft(StringBuilder(), 0) { 
        case ((sb, occurance), i) =>
          if i == 0
            ???
          else if i == str.size
            ???
          else if str(i) == str(i - 1)
            ???
          else
            ???
      }
    sb.toString
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.