Skip to content

List Set Operations — Swift 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 Swift 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. Swift port (canonical TS:
// src/lib/list-set-ops.ts; Go twin: cli/list-set-ops). Set is the membership
// structure; trim defaults on like the TS `trim !== false`.
import Foundation

struct SetOptions {
    var caseInsensitive = false
    var trim: Bool? = nil // nil -> true (the TS "omitted" default)

    var trimEnabled: Bool { trim ?? true }
}

// Folded membership key: lowercased copy when case-insensitive, else the item.
func key(_ s: String, _ o: SetOptions) -> String {
    o.caseInsensitive ? s.lowercased() : s
}

// Split a newline list into a deduped array (first-seen casing kept).
// omittingEmptySubsequences: false keeps empty lines so trim-then-drop
// matches the TS split.
func parseList(_ input: String, _ o: SetOptions) -> [String] {
    guard !input.isEmpty else { return [] } // empty input = no items
    var seen = Set<String>()
    var out: [String] = []
    for raw in input.split(separator: "\n", omittingEmptySubsequences: false) {
        var item = String(raw)
        if o.trimEnabled {
            item = item.trimmingCharacters(in: .whitespaces)
            if item.isEmpty { continue }
        }
        if seen.insert(key(item, o)).inserted { out.append(item) }
    }
    return out
}

// Compute a set op over two lists; the result is "\n"-joined.
func setOps(_ a: String, _ b: String, _ op: String, _ o: SetOptions = SetOptions()) -> String {
    let la = parseList(a, o)
    let lb = parseList(b, o)
    let ka = Set(la.map { key($0, o) })
    let kb = Set(lb.map { key($0, o) })
    var out: [String] = []
    switch op {
    case "union": // every unique item, a then b
        var seen = Set<String>()
        for s in la + lb {
            if seen.insert(key(s, o)).inserted { out.append(s) }
        }
    case "intersect": // items present in both (a's order/casing)
        out = la.filter { kb.contains(key($0, o)) }
    case "difference": // items in a not present in b
        out = la.filter { !kb.contains(key($0, o)) }
    case "symmetricDifference": // exactly one list, a-only then b-only
        out = la.filter { !kb.contains(key($0, o)) } + lb.filter { !ka.contains(key($0, o)) }
    default: break // unknown op -> ""
    }
    return out.joined(separator: "\n")
}

let a = "apple\nbanana\ncherry"
let b = "banana\ncherry\ndate"
print(setOps(a, b, "union"))               // apple\nbanana\ncherry\ndate
print(setOps(a, b, "intersect"))           // banana\ncherry
print(setOps(a, b, "difference"))          // apple
print(setOps(a, b, "symmetricDifference")) // apple\ndate
// case-folded compare, first-seen original casing emitted
print(setOps("HELLO", "hello", "intersect", 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 →