How do named capturing groups and MatchResult.destructured work in Kotlin? Show extracting fields by name and via destructuring (a, b).
answer
- Named group: (?<name>...)
- Read by name: groups["name"]?.value
- destructured = positional, groups 1..N
- destructured skips group 0
- too many components -> runtime exception
basics
~10 sName a group with (?<name>...) and read it via match.groups["name"]?.value. You can also pull groups positionally with destructuring: val (a, b) = match.destructured.
solid answer
~40 sNamed groups use the (?<name>...) syntax. After a match you read them through the MatchGroupCollection by string key: result.groups["year"]?.value, which is nullable (null if absent). Separately, MatchResult.destructured returns a Destructured object whose component1()..componentN() map to capturing groups 1..N, enabling Kotlin destructuring: val (year, month, day) = result.destructured. Note destructured is positional, NOT by name, and it skips group 0 (the whole match) — the first destructured component is group 1. Naming groups makes patterns self-documenting and robust to reordering, while destructuring is concise for fixed-arity extraction. A common combo: matchEntire(line)?.let { val (a, b) = it.destructured; ... }. Beware: destructuring more components than there are groups throws at runtime.
code
kotlin · 5 linesval rx = Regex("(?<user>\\w+)@(?<host>[\\w.]+)")
val r = rx.matchEntire("[email protected]")!!
r.groups["user"]?.value // "alice"
r.groups["host"]?.value // "example.com"
val (user, host) = r.destructured // positional: groups 1, 2go deeper
Knows (?<name>...) exists and can read it via groups["name"]?.value.
Uses both named access and positional destructuring, and knows destructured is positional starting at group 1.
Chooses named groups for maintainability/optional groups and is aware of the runtime IndexOutOfBoundsException risk with over-destructuring.
Weighs pattern readability/evolution trade-offs and codifies team conventions (named groups for shared regexes) to reduce indexing bugs.
## Named capturing groups Syntax: `(?<name>...)`. The name lets you read the group without counting parentheses: ```kotlin val date = Regex("(?<y>\\d{4})-(?<m>\\d{2})-(?<d>\\d{2})") val m = date.matchEntire("2026-06-23")!! m.groups["y"]?.value // "2026" m.groups["m"]?.value // "06" m.groups["d"]?.value // "23" ``` `groups["name"]` returns `MatchGroup?` — `null` if that named group did not participate. This is more maintainable than positional indices because inserting a new group earlier in the pattern doesn't shift the names. ## Destructuring via .destructured `MatchResult.destructured` returns a `MatchResult.Destructured` whose `component1()`, `component2()`, ... correspond to capturing **groups 1, 2, ...** (group 0, the full match, is skipped). That enables Kotlin's destructuring declaration: ```kotlin val (y, mo, d) = m.destructured // positional: groups 1,2,3 ``` Key facts: - Destructuring is **positional**, not by name — `(?<y>...)` still maps to `component1()` by position. - Requesting more components than the pattern has capturing groups throws an `IndexOutOfBoundsException` at runtime; it is not checked at compile time. - `destructured` supports up to 10 components (`component1..component10`). ## Choosing between them Use **named groups** for readability/robustness and when groups are optional or reordered. Use **destructuring** for compact, fixed-shape extraction where order is stable: ```kotlin Regex("(\\w+):(\\d+)").matchEntire("port:8080")?.let { val (key, value) = it.destructured } ``` The two compose well: name groups for clarity in the pattern, destructure when consuming.
- Does destructuring use the group names?No. It is purely positional: component1() is capturing group 1 regardless of its name.
- What happens if you destructure 4 values from a pattern with 2 groups?It compiles but throws an IndexOutOfBoundsException at runtime when the missing component is accessed.
saying these in an interview costs you the question
- Assuming destructured maps by group name
- Thinking the first destructured component is group 0 (it's group 1)
- Reading a named group expecting non-null without ?. (it is MatchGroup?)
- Destructuring more components than capturing groups
- Confusing (?<name>...) with non-capturing (?:...)