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 →