Skip to main content
Question Protected by CommunityBot
edited tags
Link
Toby Speight
  • 88.7k
  • 14
  • 104
  • 327
Added few more tags
Link
vikrant
  • 405
  • 2
  • 8
Source Link
vikrant
  • 405
  • 2
  • 8

Breaking a string expression in operator and operands

Requesting for a code review for a scala implementation.

Problem

Given an expression string made of numbers and operators +,-,/,* , break it in a list of Integer or String.

Example

"1+2+3" --> List(1, "+", 2 , "+", 3)
"+1++2++3" --> List(1, "+", 2 , "+", 3) //unary +
"1+-2+-3" --> List(1, "+", -2 , "+", -3)//unary -
"1--2--3" --> List(1, "-", -2 , "-", -3)
"-2-3*-4/-6" --> List(-2, "-", 3 , "*", -4, "/", 6)

Error checks are not needed for the valid expression, i.e. expression like *, 2+1 or // wont be passed.

Solution

I implemented the solution in Scala 2.13 . Using Either to store Int or String in the result. Wanted to check if I got algo correct for positive cases.

  def breakExpression(s: String): List[Either[Int, String]] = {
    def isValidOperator(op: String): Boolean = List("+", "-", "/", "*").contains(op)
    def isValidNumber(num:String): Boolean = num.forall(_.isDigit)

    def breakByOperator(expression:String,operator: Char): List[String] = {
      expression.split(operator).filter(_.nonEmpty).foldLeft(List.empty[String]) { case (res, e) =>
        res :+ e :+ operator.toString
      }.init
    }

    def breakByUnaryMinusOperator(expression: String): List[Int] =
      expression.split('-').foldLeft((List.empty[(String,Int)], 1)) { case ((resInProgress, multiplier), exp) =>
        if (exp.isEmpty) (resInProgress, -1)
        else  (resInProgress :+ (exp, multiplier), 1)
      }._1.map { x => x._1.toInt * x._2 }

    List('+', '/', '*').foldLeft(List(s)) { case (expressions, op) =>
      expressions flatMap { e =>
        if (isValidOperator(e) || isValidNumber(e)) List(e)
        else  breakByOperator(e, op)
      }
    }.map {
        case ss if isValidNumber(ss) => Left(ss.toInt)
        case ss => Right(ss)
    }.flatMap {
      case Left(s)  => List(Left(s))
      case Right(s) if isValidOperator(s) => List(Right(s))
      case Right(s) =>
        breakByUnaryMinusOperator(s).foldLeft(List.empty[Either[Int, String]]) { case (res, number) =>
          res :+ Left(number) :+ Right("-")
        }.init
    }
  }

Testing

Test cases

  println(breakExpression("2+34/6"))
  println( "2-3*4/6" , " ---> " , breakExpression("2-3*4/6"))
  println("2-3*-4/-6", " ---> ", breakExpression("2-3*-4/-6"))
  println("-2-3*-4/-6", " ---> ", breakExpression("-2-3*-4/-6"))
  println("1+-2--3", " ---> ", breakExpression("1+-2--3"))

I get following result

List(Left(2), Right(+), Left(34), Right(/), Left(6))
(2-3*4/6, ---> ,List(Left(2), Right(-), Left(3), Right(*), Left(4), Right(/), Left(6)))
(2-3*-4/-6, ---> ,List(Left(2), Right(-), Left(3), Right(*), Left(-4), Right(/), Left(-6)))
(-2-3*-4/-6, ---> ,List(Left(-2), Right(-), Left(3), Right(*), Left(-4), Right(/), Left(-6)))
(1+-2--3, ---> ,List(Left(1), Right(+), Left(-2), Right(-), Left(-3)))

Followup Question: What is the best way to introduce error handling for badly formed string expressions.