List Set Operations — Ruby 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 Ruby 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. Ruby 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`.
require 'set'
module ListSetOps
class SetOptions
def initialize(case_insensitive: false, trim: true)
@case_insensitive = case_insensitive
@trim = trim
end
attr_reader :case_insensitive, :trim
end
module_function
# Split a newline list into a deduped array (first-seen casing kept).
# split(..., -1) keeps trailing empty lines so trim-then-drop matches TS.
def parse_list(input, o = SetOptions.new)
return [] if input.empty? # empty input = no items
key = ->(s) { o.case_insensitive ? s.downcase : s }
seen = Set.new
input.split("\n", -1).filter_map do |raw|
item = o.trim ? raw.strip : raw
next if o.trim && item.empty?
next if seen.include?(key.call(item))
seen << key.call(item)
item
end
end
# Compute a set op over two lists; the result is "\n"-joined.
def set_ops(a, b, op, o = SetOptions.new)
key = ->(s) { o.case_insensitive ? s.downcase : s }
la = parse_list(a, o)
lb = parse_list(b, o)
ka = la.map { |s| key.call(s) }.to_set
kb = lb.map { |s| key.call(s) }.to_set
out = []
case op
when 'union' # every unique item, a then b
seen = Set.new
(la + lb).each { |s| out << s if seen.add?(key.call(s)) }
when 'intersect' then la.each { |s| out << s if kb.include?(key.call(s)) }
when 'difference' then la.each { |s| out << s unless kb.include?(key.call(s)) }
when 'symmetricDifference' # exactly one list, a-only then b-only
la.each { |s| out << s unless kb.include?(key.call(s)) }
lb.each { |s| out << s unless ka.include?(key.call(s)) }
end # unknown op -> ""
out.join("\n")
end
end
if __FILE__ == $PROGRAM_NAME
a = "apple\nbanana\ncherry"
b = "banana\ncherry\ndate"
puts ListSetOps.set_ops(a, b, 'union') # apple\nbanana\ncherry\ndate
puts ListSetOps.set_ops(a, b, 'intersect') # banana\ncherry
puts ListSetOps.set_ops(a, b, 'difference') # apple
puts ListSetOps.set_ops(a, b, 'symmetricDifference') # apple\ndate
# case-folded compare, first-seen original casing emitted
puts ListSetOps.set_ops('HELLO', 'hello', 'intersect', ListSetOps::SetOptions.new(case_insensitive: true)) # HELLO
end
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 →