SuperAPI AI Directory

small-collection-utilities

Coding & development

View repository

Updated 10-17 16:17

About this skill

Four helpers, four ways they mislead

Small enough to write from memory, which is exactly why nobody re-reads them once they are in.

Symmetric difference

infix fun <E> Collection<E>.symmetricDifference(other: Collection<E>): Set<E> {
    val left = this subtract other
    val right = other subtract this
    return left union right
}

Answers "which elements are on exactly one side" — additions and removals in one pass, when reconciling a stored list of ids against a freshly fetched one.

Position index

fun <T> Iterable<T>.indexMap(): Map<T, Int> {
    val map = mutableMapOf<T, Int>()
    forEachIndexed { i, v -> map[v] = i }
    return map
}

Turns a repeated indexOf scan inside a loop into one pass plus constant-time lookups — the fix for sorting one list into another list's order.

A tolerant parse/serialize pair for timestamped tokens

For <MM:SS.mm> word <MM:SS.mm> word …, match only the stamps and slice the text between them:

val stampRegex = Regex("""<(\d{2}):(\d{2})\.(\d{2,3})>""")
val stamps = stampRegex.findAll(payload).toList()
stamps.forEachIndexed { index, match ->
    val (minutes, seconds, fraction) = match.destructured
    val fractionMs = fraction.toLongOrNull() ?: 0L
    val timeMs = (minutes.toLongOrNull() ?: 0L) * 60_000L + (seconds.toLongOrNull() ?: 0L) * 1_000L +
        if (fraction.length == 2) fractionMs * 10L else fractionMs      // 2 digits are hundredths
    val to = if (index < stamps.size - 1) stamps[index + 1].range.first else payload.length
    val text = payload.substring(match.range.last + 1, to).trim()
    if (text.isNotBlank()) out += Token(text, timeMs)
}
if (out.isEmpty()) return null      // the caller falls back to the coarser format

Write the serializer beside it, and re-add whatever the parser stripped — noticing which parser. The line-level parse strips the space after the line stamp, and its writer puts that back:

if (lines.isNullOrEmpty() || syncType != RICH) return null    // refuse to write another format
return lines.joinToString("\n") { "[$stamp] ${it.payload}" }  // pad back the separator trim() ate

External link to deep link

fun String.toAppDeepLinkOrNull(): Uri? {
    val trimmed = trim()
    if (!trimmed.startsWith("http://", true) && !trimmed.startsWith("https://", true)) return null
    val uri = runCatching { Uri.parse(trimmed) }.getOrNull() ?: return null
    val host = uri.host?.lowercase()?.removePrefix("www.") ?: return null
    …
    return Uri.parse("yourapp://watch?v=$id")     // hand to the handler you already have
}

Traps

Symmetric difference is set arithmetic, so membership is all it can answer. Duplicates collapse, order is gone, and an element that merely moved is on both sides and reports as unchanged. Run it over models rather than ids and the answer depends on the model's equals — for a data class that is every field, so editing a title reports the item as both removed and added. Diff the ids, then compare matched pairs separately for content changes: two answers, two questions.

A position index's assignment is unconditional, so the last occurrence wins. A repeated element maps to its final position and the map comes out shorter than the list, silently. Right for a list of unique ids, wrong for anything else; build it with getOrPut if you want the first occurrence instead. Same caveat as above on the key type — keying on a model means keying on its whole equals.

A tolerant parser's trap is threefold, and the first two are silent. Matching stamp-and-text in one regex ties the text's shape to the pattern: punctuation, another angle bracket, a script the character class never considered — all dropped, and the payload comes back looking merely short. Slicing to the next delimiter has no opinion about the text at all. The fraction's digit count is its unit — read the digits as a number without checking length and every two-digit stamp lands at a tenth of where it belongs, early and plausible enough to look like a tuning problem rather than a parsing one. Every numeric field falls back rather than failing, so "unparseable" and "zero" become one answer — legitimate, but keep the whole-payload failure as null so the caller falls back to a coarser rendering instead of losing the screen. The per-slice trim()s are restored by nothing, safe only while the tokens stay a render-time value: serialize the parsed list back to text and the words run together, so re-add the separator there too, and guard on the format tag either way, since writing a payload you were not given parses into nonsense.

The temptation with an external link is to handle it — the answer is to translate it. Your existing deep-link handler already knows the special cases (which id prefixes mean which kind of collection, which need a prefix added), and a second handler starts as a copy and drifts the next time one of those rules changes. Rewrite the URL into your own scheme and hand it to the same entry point, so there is only ever one set of rules. Four details make the translator trustworthy: reject non-http(s) text before parsing at all; return null for anything unsupported, so a pasted link you do not handle becomes a search, not a failure; normalize the host before matching (lowercase(), drop a leading www.), or the same link works or fails depending on where it was copied; and decide the ambiguous case — a link carrying both an item and a collection — here, once, so every caller agrees instead of guessing differently.

Verifying it

The parse entry point should have exactly one failure mode, and it should be null:

PARSER=composeApp/src/commonMain/kotlin/com/maxrave/simpmusic/extension/RichSyncParser.kt   # your equivalent
grep -nE "return null|throw |!!|require\(|checkNotNull\(" "$PARSER"

Pass condition: every hit is return null — here, twice — never throw, !!, require( or checkNotNull(. Confirm it is the right file with grep -n "Regex(\|fraction.length" "$PARSER": the timestamp regex and the fraction-length unit check both print.

Every caller of a translator that can return null has to say what null means:

FN=toAppDeepLinkOrNull
grep -rn -A3 "\.$FN()" --include="*.kt" .

The two collection helpers are only correct over distinct elements, so look at what reaches them:

grep -rn -B2 "\.indexMap()\|symmetricDifference" --include="*.kt" .

Per hit, confirm the receiver holds ids rather than models and cannot repeat. No hits at all is its own finding: a helper nobody calls is one nobody has checked either.