Scala returning a function: multiple nested functions

I am trying to create a function that will return another function that will do multiple string operations (as determined by the given options): The options are U (uppercase), l (lowercase) T (title case) s (sorted) r (reverse) and * (remove all whitespace).

Right now, the code I have will take one of the options above (so, either U or s, etc) and return the correct result. What I want is that stringPipeline("Us*") should return a function that will convert string to uppercase, sort it and remove all whitespace.

I can't figure out how to modify my function to accommodate multiple options.

def stringPipeline(Option:String) = (str:String) => {
    val UpperCase = () => str.toUpperCase
    val LowerCase = () => str.toLowerCase
    val titleCase = () => str.split(' ').map(_.capitalize).mkString(" ")
    val reverse = () => str.reverse
    val sortChars = () => str.sorted
    val replaceChar = () => str.replaceAll("\\s","")
    Option match {
        case "U" => UpperCase()
        case "l" => LowerCase()
        case "T" => titleCase()
        case "r" => reverse()
        case "s" => sortChars()
        case "*" => replaceChar()
    }        
}

I've tried using && operator (didn't work), + operator (called both functions) and doing something like reverse(UpperCase()) which gave a compiler error.

Upvotes: 3

Views: 503

Answers (2)

Pavithran Ramachandran
Pavithran Ramachandran

Reputation: 993

An alternate approach would be to generate a composite function. You can try andThen to compose functions. For example:

scala> def inputStringPipeline(inputStr: String): String => String = {
     |     val upperCase = (input: String) => input.toUpperCase
     |     val lowerCase = (input: String) => input.toLowerCase
     |     val titleCase = (input: String) => input.split(' ').map(_.capitalize).mkString(" ")
     |     val reverse = (input: String) => input.reverse
     |     val sortChars = (input: String) => input.sorted
     |     val replaceChar = (input: String) => input.replaceAll("\\s", "")
     | 
     |     val emptyFunc: String => String = (input: String) => input
     |     inputStr.foldLeft(emptyFunc) {
     |       case (funcAcc, present) =>
     |         val newFunc = present.toString match {
     |           case "U" => upperCase
     |           case "l" => lowerCase
     |           case "T" => titleCase
     |           case "r" => reverse
     |           case "s" => sortChars
     |           case "*" => replaceChar
     |         }
     | 
     |         funcAcc andThen newFunc
     |     }
     | 
     | }
inputStringPipeline: (inputStr: String)String => String

scala> val input = "Us*"
input: String = Us*

scala> val func = inputStringPipeline(input)
func: String => String = scala.Function1$$Lambda$1061/1593722877@5ef7ae2f

scala> val str = func("age bfh dc")
str: String = ABCDEFGH

scala> println(str)
ABCDEFGH

This way a new function would be generated for a given input string operation and can be used with any number of strings. This would save time by creating the composite function once and using it again.

Upvotes: 0

Shaido
Shaido

Reputation: 28322

You could use foldLeft to recursively apply all operators on the string using a separate function that takes one option at the time.

def stringPipeline(ops: String) = (str: String) => {
  def applyOp(str: String, op: String) = op match {
    case "U" => str.toUpperCase
    case "l" => str.toLowerCase
    case "T" => str.split(' ').map(_.capitalize).mkString(" ")
    case "r" => str.reverse
    case "s" => str.sorted
    case "*" => str.replaceAll("\\s","")
  }

  ops.split("").foldLeft(str)(applyOp)
}

Testing with a string:

val str = stringPipeline("Us*")("age bfh dc")
println(str)

will give the expected result

ABCDEFGH

Upvotes: 3

Related Questions