Skip to content

List Set Operations — Kotlin source

Compute the union, intersection, difference, or symmetric difference of two newline-separated lists. Deduped results with optional trim and case-insensitive matching. Runs entirely in your browser, with a shareable link.

This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.

// list-set-ops — union / intersection / difference / symmetric difference of
// two newline-separated lists. Kotlin port (canonical TS:
// src/lib/list-set-ops.ts; Go twin: cli/list-set-ops). HashSet is the
// membership set; trim defaults on like the TS `trim !== false`.
class ListSetOps private constructor() {
    class SetOptions(val caseInsensitive: Boolean = false, val trim: Boolean = true)

    companion object {
        // Folded membership key: lowercase copy when case-insensitive, else the item.
        private fun key(s: String, o: SetOptions) = if (o.caseInsensitive) s.lowercase() else s

        // Split a newline list into a deduped list (first-seen casing kept).
        fun parseList(input: String, o: SetOptions): List<String> {
            if (input.isEmpty()) return emptyList() // empty input = no items
            val seen = HashSet<String>()
            return input.split('\n').mapNotNull { raw ->
                var item = raw
                if (o.trim) {
                    item = item.trim()
                    if (item.isEmpty()) return@mapNotNull null
                }
                if (seen.add(key(item, o))) item else null
            }
        }

        // Compute a set op over two lists; the result is "\n"-joined.
        fun setOps(a: String, b: String, op: String, o: SetOptions = SetOptions()): String {
            val la = parseList(a, o)
            val lb = parseList(b, o)
            val ka = la.mapTo(HashSet()) { key(it, o) }
            val kb = lb.mapTo(HashSet()) { key(it, o) }
            val out = ArrayList<String>()
            when (op) {
                "union" -> { // every unique item, a then b
                    val seen = HashSet<String>()
                    for (s in la + lb) if (seen.add(key(s, o))) out.add(s)
                }
                "intersect" -> for (s in la) if (key(s, o) in kb) out.add(s)
                "difference" -> for (s in la) if (key(s, o) !in kb) out.add(s)
                "symmetricDifference" -> { // exactly one list, a-only then b-only
                    for (s in la) if (key(s, o) !in kb) out.add(s)
                    for (s in lb) if (key(s, o) !in ka) out.add(s)
                }
            } // unknown op -> ""
            return out.joinToString("\n")
        }
    }
}

fun main() {
    val a = "apple\nbanana\ncherry"
    val b = "banana\ncherry\ndate"
    println(ListSetOps.setOps(a, b, "union"))               // apple\nbanana\ncherry\ndate
    println(ListSetOps.setOps(a, b, "intersect"))           // banana\ncherry
    println(ListSetOps.setOps(a, b, "difference"))          // apple
    println(ListSetOps.setOps(a, b, "symmetricDifference")) // apple\ndate
    // case-folded compare, first-seen original casing emitted
    println(ListSetOps.setOps("HELLO", "hello", "intersect", ListSetOps.SetOptions(caseInsensitive = true))) // HELLO
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →