skip to content

Explain Regex.replace with a transform lambda. How does it differ from string-replacement, and what do $1/${name} mean in the string form?

level: seniorimportance: should knowfreq 45%

answer

  1. Lambda: replace(input){ mr -> ... } returns verbatim
  2. String form: $1 / ${name} / $0 substitution
  3. No $-expansion in lambda result
  4. replaceFirst for first match only
  5. Returns new String (immutable)

basics

~10 s

replace can take a lambda that receives each MatchResult and returns the replacement text, so you compute it dynamically. The string form instead uses $1 or ${name} to insert captured groups.

solid answer

~40 s

Regex.replace has two overloads. The string form replace(input, replacement: String) substitutes every match, and in that replacement string $1, $2 (or ${name}) are back-references to captured groups, while $0 is the whole match — to output a literal dollar or backslash you escape with a backslash. The lambda form replace(input) { matchResult -> String } gives full control: for each MatchResult you can read groupValues/groups, run arbitrary Kotlin, and return any String (no $-substitution happens in the lambda's return value). Use the lambda when the replacement depends on the matched content (e.g. uppercasing, arithmetic, lookups); use the string form for simple structural rewrites. There's also replaceFirst for only the first match. Both return a new String — Kotlin strings are immutable, so the original is untouched.

code

kotlin · 7 lines
kotlin
// dynamic replacement via lambda
val masked = Regex("\\d{4}").replace("card 1234 5678") { "*".repeat(it.value.length) }
// "card **** ****"

// group reordering via string form
val swapped = Regex("(?<d>\\d{2})/(?<m>\\d{2})").replace("23/06", "\${m}-\${d}")
// "06-23"

go deeper

for a junior

Knows replace exists and can do a simple string replacement.

for a middle

Uses the lambda overload to compute replacements and reads $1/${name} in the string form.

for a senior

Chooses the right overload, understands no $-substitution in the lambda, and handles escaping/immutability correctly.

for a principal

Considers injection/escaping safety of user-supplied replacement text and favors the lambda form to eliminate substitution-token risks; reasons about performance over large inputs.

## Two replace overloads ### 1. String replacement (with substitution syntax) ```kotlin fun replace(input: CharSequence, replacement: String): String ``` In the `replacement` string, special tokens expand: - `$1`, `$2`, ... — the text of capturing group 1, 2, ... - `${name}` — a named group's text. - `$0` — the entire match. - To emit a literal `$` or `\`, escape it with a backslash inside the replacement. ```kotlin Regex("(\\w+)@(\\w+)").replace("a@b", "$2.$1") // "b.a" ``` ### 2. Transform lambda ```kotlin fun replace(input: CharSequence, transform: (MatchResult) -> CharSequence): String ``` For each match you get the `MatchResult` and return the replacement. **No `$`-substitution is applied to the lambda's result** — the returned string is used verbatim — so you have arbitrary Kotlin at your disposal: ```kotlin val shout = Regex("\\w+").replace("keep it quiet") { it.value.uppercase() } // "KEEP IT QUIET" val bumped = Regex("\\d+").replace("v1 v2") { (it.value.toInt() + 1).toString() } // "v2 v3" ``` ## When to use which - **String form**: simple, declarative reordering/wrapping of captured groups; fast to read. - **Lambda form**: replacement value depends on computation, external lookups, conditionals, or per-match formatting. ## Related and important notes - **`replaceFirst(input, replacement)`** replaces only the first match. - All return a **new** `String`; Kotlin `String` is immutable, so the input is never mutated. - Watch the `$`/`\` escaping in the string form — an unescaped `$` followed by a digit you didn't intend is a classic bug; the lambda form sidesteps escaping entirely, which is why it's safer for user-derived replacement text.

  • Does the lambda's returned string undergo $1 substitution?
    No. The lambda result is used literally; $-back-references only apply to the String replacement overload.
  • How do you insert a literal $ using the String replacement form?
    Escape it with a backslash in the replacement string so it isn't read as a group back-reference.

saying these in an interview costs you the question

  • Expecting $1 to work inside the lambda's return value
  • Forgetting replace returns a new String (thinking it mutates)
  • Not escaping $ in the string-replacement form
  • Using string form for replacements that need computation
  • Confusing replace (all matches) with replaceFirst

context