Reading — step 1 of 7
Learn
Clojure has first-class sets: unordered collections of unique values. The clojure.set namespace provides operations like union, intersection, difference. "Clojure for the Brave and True" introduces sets as one of the four core collection types.
Creating sets
#{1 2 3} ;; literal set
(set [1 1 2 3 3]) ;; #{1 2 3} — dedupe a list
(hash-set 1 2 3) ;; explicit constructor
(sorted-set 3 1 2) ;; #{1 2 3} — ordered for iteration
Set literals dedupe at read time. (set ...) is the conversion function.
Membership and access
(contains? #{1 2 3} 2) ;; true
(#{1 2 3} 2) ;; 2 — sets are FUNCTIONS of their members
(#{1 2 3} 99) ;; nil — not a member
Sets used as functions return the value if present, nil otherwise. The idiomatic membership test uses contains? for clarity, but the function-call form is concise:
(filter #{1 2 3} [1 2 3 4 5 6]) ;; (1 2 3) — keep only set members
Very clean filter pattern.
Manipulation
(conj #{1 2 3} 4) ;; #{1 2 3 4} — add
(conj #{1 2 3} 2) ;; #{1 2 3} — already there, no change
(disj #{1 2 3} 2) ;; #{1 3} — remove
conj adds; disj removes. Both return new sets (immutable).
Set operations — clojure.set
(require '[clojure.set :as set])
(set/union #{1 2 3} #{3 4 5}) ;; #{1 2 3 4 5}
(set/intersection #{1 2 3} #{2 3 4}) ;; #{2 3}
(set/difference #{1 2 3 4} #{2 4}) ;; #{1 3}
(set/subset? #{1 2} #{1 2 3}) ;; true
(set/superset? #{1 2 3} #{1 2}) ;; true
Classic set algebra. Useful for permission checks, tag matching, deduplication logic.
When to use sets vs maps vs vectors
- Set — uniqueness matters; you want fast membership tests; order doesn't matter
- Map — key-value pairs; lookups by key
- Vector — ordered, indexed access
- List — sequential, prepend-friendly (mostly used as code/data; vectors more common for data)
Complexity
Clojure sets (and maps) are persistent hash tries:
- Membership / add / remove: effectively O(1) (technically O(log32 n))
- Iteration: O(n)
- Equality: O(n)
Much faster than (some #{x} coll) over a vector — sets are designed for this.
Sets in real code
;; Permissions check:
(def admin-perms #{:read :write :delete})
(contains? admin-perms :delete)
;; Dedupe:
(distinct [1 1 2 2 3 3]) ;; (1 2 3) — but distinct is lazy; for sets:
(set [1 1 2 2 3 3]) ;; #{1 2 3} — eager
;; Tag matching:
(def tags-on-post #{:tech :clojure :tutorial})
(def my-interests #{:clojure :rust})
(set/intersection tags-on-post my-interests) ;; #{:clojure}
Common mistakes
- Set as filter without grasping the trick —
(filter #{1 2 3} coll)keeps members only because non-members map to nil (falsy). - Expecting set order — sets are unordered (except sorted-set). Don't depend on iteration order.
disjon a vector or list — only works on sets. Other collections needremoveorfilter.- Mixing types in a set — perfectly fine:
#{1 "a" :keyword}is a valid set with three elements of different types. some-with-set vscontains?— both work;contains?is clearer for intent.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…