Uniq — Unique Collections
JavaScript’s native Set API is mutable: .add() and .delete() modify collections in place, requiring defensive copying (new Set(existing)) to prevent accidental mutations across function boundaries.
Uniq provides curried, immutable operations over sets. Every operation returns a fresh collection, preserving the original set.
The problem with mutable sets
Section titled “The problem with mutable sets”Managing permission sets without immutable operations requires manual copying:
If we forget to make this copy, we introduce a bug where the user permanently gains the "admin" permission. The native Set API conflates the identity of the collection with its current state.
The shift to immutable uniqueness
Section titled “The shift to immutable uniqueness”Uniq separates identity from state. Every modification returns a new set representing the new state, while the original remains unchanged. Furthermore, Uniq is designed to be highly efficient: if an operation would result in no change (such as inserting a value that is already present, or removing a value that is absent), Uniq returns the original set reference, avoiding unnecessary memory allocations.
flowchart TD
A["Uniq (Original)"] --> B["insert('admin')"]
B --> C["New Uniq (With admin)"]
A --> D["Original Uniq (Unchanged)"]
Creating collections
Section titled “Creating collections”We can lift raw arrays or individual elements into an immutable Uniq collection using constructors:
Checking membership
Section titled “Checking membership”To inspect the contents of a collection, we use Uniq.has and Uniq.isSubsetOf. Because these functions are curried and place the collection as the last argument, they fit cleanly into composition pipelines:
Adding and removing items
Section titled “Adding and removing items”Adding or removing items returns a new Uniq collection. If the operation does not change the membership of the set, the original reference is preserved:
Transforming and filtering collections
Section titled “Transforming and filtering collections”We can transform the elements of a collection or filter them using pure functions. If a transformation produces duplicate values, Uniq automatically merges them to maintain uniqueness:
Classic set operations
Section titled “Classic set operations”Uniq provides pure, immutable implementations of classic set algebra operations: union, intersection, and difference. These are useful when reconciling permissions, merging configuration profiles, or calculating differentials between two states.
Folding and converting to standard types
Section titled “Folding and converting to standard types”When it is time to leave the immutable context — either to serialize data for an API or to interface with a library that requires standard arrays — we can fold or convert our collection:
Problems it solves
Section titled “Problems it solves”- Managing unique collections in immutable state trees: In React, Vue, or Zustand state models, mutating native
Setobjects in place bypasses change detection and causes UI rendering bugs.Uniqenforces structural immutability, returning fresh references on updates while reusing existing references when operations produce no changes. - Permission algebra and role reconciliation: In role-based access control (RBAC), applications frequently need to compute missing permissions (
Uniq.difference), combine group privileges (Uniq.union), or find overlapping scopes (Uniq.intersection).Uniqprovides pure, point-free set algebra operations. - Entitlement and prerequisite checks (
Uniq.subset): Verifying that a user’s permission set covers all required scopes for an API endpoint or that a cart’s tags meet promotional prerequisites in a single declarative comparison. - Deduplicating tag lists and selected filters: In search filter components and tag editors, adding, toggling, or removing selected items inside
pipepipelines without managing temporary arrays or mutable set instances. - Point-free set mapping and aggregation: Transforming or filtering unique collections (such as mapping user IDs to names while maintaining uniqueness) with
Uniq.map,Uniq.filter, andUniq.reduce.