Scala - String Operations with Examples

• Scala strings are immutable Java String objects with enhanced functionality through implicit conversions to StringOps, providing functional programming methods like map, filter, and fold

Key Insights

• Scala strings are immutable Java String objects with enhanced functionality through implicit conversions to StringOps, providing functional programming methods like map, filter, and fold • String interpolation in Scala offers three built-in interpolators (s, f, raw) and supports custom interpolators for domain-specific formatting needs • Pattern matching with regular expressions and extractors enables powerful string parsing and transformation capabilities beyond traditional imperative approaches

String Basics and Immutability

Scala strings are instances of java.lang.String, making them immutable by design. Every string operation returns a new string rather than modifying the original.

val original = "Hello"
val modified = original.concat(" World")

println(original)  // Hello
println(modified)  // Hello World

// Common operations
val text = "Scala Programming"
println(text.length)           // 17
println(text.toLowerCase)      // scala programming
println(text.substring(0, 5))  // Scala
println(text.replace("a", "A")) // ScAlA ProgrAmming

Scala enriches Java strings through implicit conversion to StringOps, providing functional methods:

val str = "functional"

// Using functional methods
str.filter(_.isUpper)           // ""
str.map(_.toUpper)              // "FUNCTIONAL"
str.exists(_.isDigit)           // false
str.forall(_.isLetter)          // true
str.take(4)                     // "func"
str.drop(4)                     // "tional"

String Interpolation

Scala provides three built-in interpolators that prefix string literals.

s-interpolator for variable substitution:

val name = "Alice"
val age = 30
val greeting = s"Hello, $name! You are $age years old."
println(greeting)  // Hello, Alice! You are 30 years old.

// Expression evaluation
val result = s"Next year you'll be ${age + 1}."
println(result)  // Next year you'll be 31.

// Accessing object properties
case class Person(firstName: String, lastName: String)
val person = Person("John", "Doe")
println(s"Full name: ${person.firstName} ${person.lastName}")

f-interpolator for formatted output:

val price = 19.99
val quantity = 3
val total = price * quantity

println(f"Price: $$${price}%.2f")           // Price: $19.99
println(f"Quantity: $quantity%d")            // Quantity: 3
println(f"Total: $$${total}%.2f")           // Total: $59.97

// Padding and alignment
val items = List(("Apple", 1.50), ("Banana", 0.75), ("Orange", 2.25))
items.foreach { case (name, price) =>
  println(f"$name%-10s $$${price}%6.2f")
}
// Apple      $  1.50
// Banana     $  0.75
// Orange     $  2.25

raw-interpolator to avoid escape sequences:

println(s"Line1\nLine2")    // Two lines
println(raw"Line1\nLine2")  // Line1\nLine2 (literal)

val path = raw"C:\Users\Documents\file.txt"
println(path)  // C:\Users\Documents\file.txt

Custom String Interpolators

Create domain-specific interpolators for specialized formatting:

implicit class JsonInterpolator(val sc: StringContext) extends AnyVal {
  def json(args: Any*): String = {
    val strings = sc.parts.iterator
    val expressions = args.iterator
    val buf = new StringBuilder(strings.next())
    
    while(strings.hasNext) {
      val arg = expressions.next()
      val formatted = arg match {
        case s: String => s""""$s""""
        case n: Number => n.toString
        case b: Boolean => b.toString
        case _ => s""""$arg""""
      }
      buf.append(formatted)
      buf.append(strings.next())
    }
    buf.toString
  }
}

val name = "Bob"
val active = true
val score = 95

val jsonStr = json"""{"name": $name, "active": $active, "score": $score}"""
println(jsonStr)  // {"name": "Bob", "active": true, "score": 95}

Regular Expressions and Pattern Matching

Scala’s Regex class provides powerful pattern matching capabilities:

import scala.util.matching.Regex

val pattern: Regex = "[0-9]+".r
val text = "Order 123 shipped, invoice 456 pending"

// Find first match
pattern.findFirstIn(text) match {
  case Some(num) => println(s"First number: $num")  // First number: 123
  case None => println("No match")
}

// Find all matches
val numbers = pattern.findAllIn(text).toList
println(numbers)  // List(123, 456)

// Replace matches
val masked = pattern.replaceAllIn(text, "XXX")
println(masked)  // Order XXX shipped, invoice XXX pending

Extracting groups:

val emailPattern = """(\w+)@(\w+\.\w+)""".r

"user@example.com" match {
  case emailPattern(user, domain) =>
    println(s"User: $user, Domain: $domain")  // User: user, Domain: example.com
  case _ => println("Invalid email")
}

// Multiple extractions
val logEntry = "2024-01-15 ERROR Database connection failed"
val logPattern = """(\d{4}-\d{2}-\d{2})\s+(\w+)\s+(.+)""".r

logEntry match {
  case logPattern(date, level, message) =>
    println(s"Date: $date\nLevel: $level\nMessage: $message")
}

String Splitting and Joining

Efficient string tokenization and concatenation:

val csv = "apple,banana,cherry,date"

// Split operations
val fruits = csv.split(",")
println(fruits.mkString(" | "))  // apple | banana | cherry | date

// Split with limit
val limited = csv.split(",", 3)
println(limited.toList)  // List(apple, banana, cherry,date)

// Split on whitespace
val sentence = "The quick   brown fox"
val words = sentence.split("\\s+")
println(words.toList)  // List(The, quick, brown, fox)

Joining collections:

val items = List("Scala", "Java", "Kotlin", "Groovy")

println(items.mkString(", "))           // Scala, Java, Kotlin, Groovy
println(items.mkString("[", ", ", "]")) // [Scala, Java, Kotlin, Groovy]

// Using StringBuilder for efficiency
val builder = new StringBuilder
items.foreach(item => builder.append(item).append("; "))
println(builder.toString)  // Scala; Java; Kotlin; Groovy;

Advanced String Operations

Multi-line strings and stripMargin:

val query = """SELECT id, name, email
              |FROM users
              |WHERE active = true
              |ORDER BY name""".stripMargin

println(query)

// Custom margin character
val json = """{
             #  "name": "Product",
             #  "price": 29.99,
             #  "available": true
             #}""".stripMargin('#')

String comparison and ordering:

val str1 = "apple"
val str2 = "banana"

println(str1 == str2)              // false
println(str1.equalsIgnoreCase("APPLE"))  // true
println(str1.compareTo(str2))      // negative (apple < banana)

// Custom ordering
case class Product(name: String, price: Double)
implicit val productOrdering: Ordering[Product] = 
  Ordering.by(_.name)

val products = List(
  Product("Laptop", 999.99),
  Product("Mouse", 29.99),
  Product("Keyboard", 79.99)
)

println(products.sorted.map(_.name))  // List(Keyboard, Laptop, Mouse)

Checking string properties:

val input = "Scala2024"

println(input.forall(_.isLetterOrDigit))  // true
println(input.exists(_.isDigit))          // true
println(input.count(_.isDigit))           // 4
println(input.startsWith("Scala"))        // true
println(input.endsWith("2024"))           // true
println(input.contains("la"))             // true

// Partition based on predicate
val (letters, digits) = input.partition(_.isLetter)
println(s"Letters: $letters, Digits: $digits")  // Letters: Scala, Digits: 2024

String operations in Scala combine Java’s robust string handling with functional programming paradigms. The implicit conversion to StringOps enables method chaining and collection-like operations, while string interpolation and pattern matching provide expressive syntax for common tasks. Understanding these features allows you to write concise, readable code for text processing and manipulation.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.