Using findAll and MatchResult, how do you extract all matches and their captured groups? Explain what groupValues contains and the index convention.
answer
- findAll -> Sequence<MatchResult> (lazy)
- groupValues[0] = whole match
- groups[1]?.value = nullable group
- non-matching group -> "" in groupValues
- .range gives index span
basics
~10 sfindAll returns a lazy sequence of all matches. Each MatchResult has groupValues, a list where index 0 is the whole match and index 1+ are the captured groups in order.
solid answer
~40 sfindAll(input): Sequence<MatchResult> lazily yields every non-overlapping match. Iterate it (or call .toList()) and read each MatchResult. groupValues is a List<String> where [0] is the entire matched text and [1], [2], ... are the capturing groups left-to-right. A group that didn't participate yields an empty string "" in groupValues. For nullable access use the groups collection: result.groups[1]?.value (returns null if the optional group didn't match) and result.groups[1]?.range for its position. result.value equals groupValues[0]; result.range is the span in the input. Because findAll is a lazy Sequence, you get short-circuiting with operators like first/takeWhile without scanning the whole string. Use destructuring (val (a, b) = result.destructured) to pull groups 1..N concisely.
code
kotlin · 7 linesval log = Regex("\\[(\\w+)] (.+)")
val line = "[INFO] started"
val m = log.matchEntire(line)!!
m.groupValues[0] // "[INFO] started"
m.groupValues[1] // "INFO"
m.groupValues[2] // "started"
m.groups[1]?.range // 1..4go deeper
Can iterate findAll and read groupValues[1] to extract a captured value.
Explains the 0=whole/1+=groups index convention, the lazy Sequence return, and value/range on MatchResult.
Distinguishes groupValues empty-string semantics from groups nullable semantics and leverages laziness for short-circuiting.
Reasons about overlapping-match limitations, capture-group indexing maintenance risk, and when named groups improve resilience to pattern edits.
## findAll: a lazy sequence of matches ```kotlin fun findAll(input: CharSequence, startIndex: Int = 0): Sequence<MatchResult> ``` It returns a **lazy** `Sequence<MatchResult>` of all non-overlapping matches. Lazy means matches are computed on demand, so `findAll(text).first { ... }` stops early. Materialize with `.toList()` if you need a list. ## MatchResult anatomy Each match is a `MatchResult` exposing: - **`value: String`** — the entire matched substring (same as `groupValues[0]`). - **`range: IntRange`** — the start..end index span in the input. - **`groupValues: List<String>`** — index `0` = the whole match; `1, 2, ...` = capturing groups in left-to-right order of their `(` parentheses. A group that did **not** participate appears as `""` (empty string), so `groupValues` never contains null. - **`groups: MatchGroupCollection`** — index access returns `MatchGroup?`. `groups[1]?.value` is the matched text and `groups[1]?.range` its span; this is the nullable-aware alternative, distinguishing "matched empty" from "did not match". - **`next(): MatchResult?`** — the next match (what `findAll` walks internally). ```kotlin val kv = Regex("(\\w+)=(\\d+)") val text = "a=1 b=22 c=333" val pairs = kv.findAll(text).map { m -> val key = m.groupValues[1] // capturing group 1 val num = m.groupValues[2] // capturing group 2 key to num.toInt() }.toList() // [(a,1),(b,22),(c,333)] ``` ## Index convention pitfall The most common mistake is forgetting that **`groupValues[0]` is the full match**, so the first capturing group is `[1]`, not `[0]`. Also, **non-capturing groups** `(?:...)` do not get an index. Optional groups returning `""` in `groupValues` vs `null` in `groups[i]?.value` matters when an empty match is semantically valid.
- Why might you use result.groups[i]?.value instead of groupValues[i]?groups distinguishes a group that matched empty from one that never participated (null), whereas groupValues collapses both into an empty string.
- Is findAll's result eager or lazy, and why does it matter?Lazy (a Sequence): operators like first/takeWhile can short-circuit without scanning the entire input or materializing all matches.
saying these in an interview costs you the question
- Reading the first capture group as groupValues[0]
- Expecting findAll to return a List instead of a Sequence
- Assuming non-matching optional groups give null in groupValues (they give "")
- Counting non-capturing (?:...) groups when indexing
- Calling .toList() unnecessarily and losing short-circuiting