HashSet
HashSet
An immutable HashSet provides a collection of unique values with efficient
lookup, insertion and removal. Once created, a HashSet cannot be modified;
any operation that would alter the set instead returns a new HashSet with
the changes. This immutability offers benefits like predictable state
management and easier reasoning about your code.
What Problem Does It Solve?
HashSet solves the problem of maintaining an unsorted collection where each
value appears exactly once, with fast operations for checking membership and
adding/removing values.
When to Use
Use HashSet when you need:
- A collection with no duplicate values
- Efficient membership testing (
O(1)average complexity) - Set operations like union, intersection, and difference
- An immutable data structure that preserves functional programming patterns
Advanced Features
HashSet provides operations for:
- Transforming sets with map and flatMap
- Filtering elements with filter
- Combining sets with union, intersection and difference
- Performance optimizations via mutable operations in controlled contexts
Performance Characteristics
- Lookup operations (:HashSet.has):
O(1)average time complexity - Insertion operations (:HashSet.add):
O(1)average time complexity - Removal operations (:HashSet.remove):
O(1)average time complexity - Set operations (:HashSet.union,
:HashSet.intersection):
O(n)where n is the size of the smaller set - Iteration:
O(n)where n is the size of the set
The HashSet data structure implements the following traits:
- Iterable: allows iterating over the values in the set
- Equal: allows comparing two sets for value-based equality
- Pipeable: allows chaining operations with the pipe operator
- Inspectable: allows inspecting the contents of the set
Operations Reference
| Category | Operation | Description | Complexity |
|---|---|---|---|
| constructors | :HashSet.empty | Creates an empty HashSet | O(1) |
| constructors | :HashSet.fromIterable | Creates a HashSet from an iterable | O(n) |
| constructors | :HashSet.make | Creates a HashSet from multiple values | O(n) |
| elements | :HashSet.has | Checks if a value exists in the set | O(1) avg |
| elements | :HashSet.some | Checks if any element satisfies a predicate | O(n) |
| elements | :HashSet.every | Checks if all elements satisfy a predicate | O(n) |
| elements | :HashSet.isSubset | Checks if a set is a subset of another | O(n) |
| getters | :HashSet.values | Gets an iterator of all values | O(1) |
| getters | :HashSet.toValues | Gets an array of all values | O(n) |
| getters | :HashSet.size | Gets the number of elements | O(1) |
| mutations | :HashSet.add | Adds a value to the set | O(1) avg |
| mutations | :HashSet.remove | Removes a value from the set | O(1) avg |
| mutations | :HashSet.toggle | Toggles a value's presence | O(1) avg |
| operations | :HashSet.difference | Computes set difference (A - B) | O(n) |
| operations | :HashSet.intersection | Computes set intersection (A ∩ B) | O(n) |
| operations | :HashSet.union | Computes set union (A ∪ B) | O(n) |
| mapping | :HashSet.map | Transforms each element | O(n) |
| sequencing | :HashSet.flatMap | Transforms and flattens elements | O(n) |
| traversing | :HashSet.forEach | Applies a function to each element | O(n) |
| folding | :HashSet.reduce | Reduces the set to a single value | O(n) |
| filtering | :HashSet.filter | Keeps elements that satisfy a predicate | O(n) |
| partitioning | :HashSet.partition | Splits into two sets by a predicate | O(n) |
Notes
Composability with the Effect Ecosystem:
This HashSet is designed to work seamlessly within the Effect ecosystem. It
implements the Iterable, Equal, Pipeable, and
Inspectable traits from Effect. This ensures compatibility with other
Effect data structures and functionalities. For example, you can easily use
Effect's pipe method to chain operations on the HashSet.
Equality of Elements with Effect's Equal Trait:
This HashSet relies on Effect's Equal trait to determine the
uniqueness of elements within the set. The way equality is checked depends on
the type of the elements:
- Primitive Values: For primitive JavaScript values like strings, numbers,
booleans,
null, andundefined, equality is determined by their value (similar to the===operator). - Objects and Custom Types: For objects and other custom types, equality is
determined by whether those types implement the Equal interface
themselves. If an element type implements
Equal, theHashSetwill delegate to that implementation to perform the equality check. This allows you to define custom logic for determining when two instances of your objects should be considered equal based on their properties, rather than just their object identity.
Simplifying Equality and Hashing with Data and Schema:
Effect's Data and Schema `Schema.Data` modules offer powerful ways to automatically handle the implementation of both the Equal and Hash traits for your custom data structures.
-
DataModule: By using constructors likeData.struct,Data.tuple,Data.array, orData.caseto define your data types, Effect automatically generates the necessary implementations for value-based equality and consistent hashing. This significantly reduces boilerplate and ensures correctness. -
SchemaModule: When defining data schemas using the Schema module, you can useSchema.Datato automatically include theEqualandHashtraits in the decoded objects. This is particularly important when working withHashSet. For decoded objects to be correctly recognized as equal within aHashSet, ensure that the schema for those objects is defined usingSchema.Data.
Interoperability with the JavaScript Runtime:
To interoperate with the regular JavaScript runtime, Effect's HashSet
provides methods to access its elements in formats readily usable by
JavaScript APIs: HashSet.values,
HashSet.toValues
Be mindful of performance implications (both time and space complexity) when frequently converting between Effect's immutable HashSet and mutable JavaScript data structures, especially for large collections.
Example
import { Equal, Hash, HashSet } from "effect"
class Person implements Equal.Equal { constructor( readonly id: number, // Unique identifier readonly name: string, readonly age: number ) {}
// Define equality based on id, name, and age [Equal.symbol](that: Equal.Equal): boolean { if (that instanceof Person) { return ( Equal.equals(this.id, that.id) && Equal.equals(this.name, that.name) && Equal.equals(this.age, that.age) ) } return false }
// Generate a hash code based on the unique id [Hash.symbol](): number { return Hash.hash(this.id) }}
// Creating a HashSet with objects that implement the Equal interfaceconst set = HashSet.empty().pipe( HashSet.add(new Person(1, "Alice", 30)), HashSet.add(new Person(1, "Alice", 30)))
// HashSet recognizes them as equal, so only one element is storedconsole.log(HashSet.size(set))// Output: 1Example
import { HashSet, Data, Equal } from "effect"import assert from "node:assert/strict"
// Data.* implements the `Equal` traits for usconst person1 = Data.struct({ id: 1, name: "Alice", age: 30 })const person2 = Data.struct({ id: 1, name: "Alice", age: 30 })
assert(Equal.equals(person1, person2))
const set = HashSet.empty().pipe( HashSet.add(person1), HashSet.add(person2))
// HashSet recognizes them as equal, so only one element is storedconsole.log(HashSet.size(set)) // Output: 1Example
import { Equal, HashSet, Schema } from "effect"import assert from "node:assert/strict"
// Schema.Data implements the `Equal` traits for usconst PersonSchema = Schema.Data( Schema.Struct({ id: Schema.Number, name: Schema.String, age: Schema.Number }))
const Person = Schema.decode(PersonSchema)
const person1 = Person({ id: 1, name: "Alice", age: 30 })const person2 = Person({ id: 1, name: "Alice", age: 30 })
assert(Equal.equals(person1, person2)) // Output: true
const set = HashSet.empty().pipe( HashSet.add(person1), HashSet.add(person2))
// HashSet thanks to Schema.Data implementation of the `Equal` trait, recognizes the two Person as equal, so only one element is storedconsole.log(HashSet.size(set)) // Output: 1Example
import { HashSet } from "effect"
const hashSet: HashSet.HashSet<number> = HashSet.make(1, 2, 3)
// Using HashSet.values to convert HashSet.HashSet<A> to IterableIterator<A>const iterable: IterableIterator<number> = HashSet.values(hashSet)
console.log(...iterable) // Logs: 1 2 3
// Using HashSet.toValues to convert HashSet.HashSet<A> to Array<A>const array: Array<number> = HashSet.toValues(hashSet)
console.log(array) // Logs: [ 1, 2, 3 ]Constructors
Creates an empty HashSet.
Time complexity: O(1)
See
- Other
HashSetconstructors are :HashSet.make :HashSet.fromIterable
Signature
declare const empty: <A = never>() => HashSet<A>Example
import { HashSet, pipe } from "effect"
console.log( pipe( // Provide a type argument to create a HashSet of a specific type HashSet.empty<number>(), HashSet.add(1), HashSet.add(1), // Notice the duplicate HashSet.add(2), HashSet.toValues )) // Output: [1, 2]fromIterable
Creates a new HashSet from an iterable collection of values.
Time complexity: O(n) where n is the number of elements in the iterable
See
- Other
HashSetconstructors are :HashSet.empty :HashSet.make
Signature
declare const fromIterable: <A>(elements: Iterable<A>) => HashSet<A>Example
// Creating a HashSet from an Arrayimport { HashSet, pipe } from "effect"
console.log( pipe( [1, 2, 3, 4, 5, 1, 2, 3], // Array<number> is an Iterable<number>; Note the duplicates. HashSet.fromIterable, HashSet.toValues )) // Output: [1, 2, 3, 4, 5]Example
// Creating a HashSet from a Setimport { HashSet, pipe } from "effect"
console.log( pipe( new Set(["apple", "banana", "orange", "apple"]), // Set<string> is an Iterable<string> HashSet.fromIterable, HashSet.toValues )) // Output: ["apple", "banana", "orange"]Example
// Creating a HashSet from a Generatorimport { HashSet } from "effect"
// Generator functions return iterablesfunction* fibonacci(n: number): Generator<number, void, unknown> { let [a, b] = [0, 1] for (let i = 0; i < n; i++) { yield a ;[a, b] = [b, a + b] }}
// Create a HashSet from the first 10 Fibonacci numbersconst fibonacciSet = HashSet.fromIterable(fibonacci(10))
console.log(HashSet.toValues(fibonacciSet))// Outputs: [0, 1, 2, 3, 5, 8, 13, 21, 34] but in unsorted orderExample
// Creating a HashSet from another HashSetimport { HashSet, pipe } from "effect"
console.log( pipe( // since HashSet implements the Iterable interface, we can use it to create a new HashSet HashSet.make(1, 2, 3, 4), HashSet.fromIterable, HashSet.toValues // turns the HashSet back into an array )) // Output: [1, 2, 3, 4]Example
// Creating a HashSet from other Effect's data structures like Chunkimport { Chunk, HashSet, pipe } from "effect"
console.log( pipe( Chunk.make(1, 2, 3, 4), // Iterable<number> HashSet.fromIterable, HashSet.toValues // turns the HashSet back into an array )) // Outputs: [1, 2, 3, 4]Construct a new HashSet from a variable number of values.
Time complexity: O(n) where n is the number of elements
See
- Other
HashSetconstructors are :HashSet.fromIterable :HashSet.empty
Signature
declare const make: <As extends ReadonlyArray<any>>(...elements: As) => HashSet<As[number]>Example
import { Equal, Hash, HashSet, pipe } from "effect"import assert from "node:assert/strict"
class Character implements Equal.Equal { readonly name: string readonly trait: string
constructor(name: string, trait: string) { this.name = name this.trait = trait }
// Define equality based on name, and trait [Equal.symbol](that: Equal.Equal): boolean { if (that instanceof Character) { return ( Equal.equals(this.name, that.name) && Equal.equals(this.trait, that.trait) ) } return false }
// Generate a hash code based on the sum of the character's name and trait [Hash.symbol](): number { return Hash.hash(this.name + this.trait) }
static readonly of = (name: string, trait: string): Character => { return new Character(name, trait) }}
assert.strictEqual( Equal.equals( HashSet.make( Character.of("Alice", "Curious"), Character.of("Alice", "Curious"), Character.of("White Rabbit", "Always late"), Character.of("Mad Hatter", "Tea enthusiast") ), // Is the same as adding each character to an empty set pipe( HashSet.empty(), HashSet.add(Character.of("Alice", "Curious")), HashSet.add(Character.of("Alice", "Curious")), // Alice tried to attend twice! HashSet.add(Character.of("White Rabbit", "Always late")), HashSet.add(Character.of("Mad Hatter", "Tea enthusiast")) ) ), true, "`HashSet.make` and `HashSet.empty() + HashSet.add()` should be equal")
assert.strictEqual( Equal.equals( HashSet.make( Character.of("Alice", "Curious"), Character.of("Alice", "Curious"), Character.of("White Rabbit", "Always late"), Character.of("Mad Hatter", "Tea enthusiast") ), HashSet.fromIterable([ Character.of("Alice", "Curious"), Character.of("Alice", "Curious"), Character.of("White Rabbit", "Always late"), Character.of("Mad Hatter", "Tea enthusiast") ]) ), true, "`HashSet.make` and `HashSet.fromIterable` should be equal")Elements
Check if a predicate holds true for every HashSet element.
Time complexity is O(n) as it needs to traverse the whole HashSet
collection
See
- Other
HashSetelements are :HashSet.has :HashSet.some :HashSet.isSubset
Signature
declare const every: { <A, B>(refinement: Refinement<NoInfer<A>, B>): (self: HashSet<A>) => self is HashSet<B>; <A>(predicate: Predicate<A>): (self: HashSet<A>) => boolean; <A, B>(self: HashSet<A>, refinement: Refinement<A, B>): self is HashSet<B>; <A>(self: HashSet<A>, predicate: Predicate<A>): boolean;}Example
// Syntax with Refinementimport { HashSet, pipe, Predicate } from "effect"
const numberOrString = HashSet.make(1, "1", "one", "uno")
// with `data-last`, a.k.a. `pipeable` API and `Refinement`pipe( numberOrString, // HashSet.HashSet<number | string> HashSet.every(Predicate.isString)) // HashSet.HashSet<string>
// or piped with the pipe function and `Refinement`numberOrString // HashSet.HashSet<number | string> .pipe(HashSet.every(Predicate.isString)) // HashSet.HashSet<string>
// or with `data-first` API and `Refinement`HashSet.every( numberOrString, // HashSet.HashSet<number | string> Predicate.isString) // HashSet.HashSet<string>Example
// Syntax with Predicateimport { HashSet, pipe } from "effect"
const set = HashSet.make(1, 2, 3)
// with `data-last`, a.k.a. `pipeable` APIpipe( set, HashSet.every((n) => n >= 0)) // true
// or piped with the pipe functionset.pipe(HashSet.every((n) => n >= 0)) // true
// or with `data-first` APIHashSet.every(set, (n) => n >= 0) // trueChecks if the specified value exists in the HashSet.
Time complexity: O(1) average
See
- Other
HashSetelements are :HashSet.some :HashSet.every :HashSet.isSubset
Signature
declare const has: { <A>(value: A): (self: HashSet<A>) => boolean; <A>(self: HashSet<A>, value: A): boolean;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` APIpipe(HashSet.make(0, 1, 2), HashSet.has(3)) // false
// or piped with the pipe functionHashSet.make(0, 1, 2).pipe(HashSet.has(3)) // false
// or with `data-first` APIHashSet.has(HashSet.make(0, 1, 2), 3) // falseReturns true if and only if every element in the this HashSet is an
element of the second set,
NOTE: the hash and equal of both sets must be the same.
Time complexity analysis is of O(n)
See
- Other
HashSetelements are :HashSet.has :HashSet.some :HashSet.every
Signature
declare const isSubset: { <A>(that: HashSet<A>): (self: HashSet<A>) => boolean; <A>(self: HashSet<A>, that: HashSet<A>): boolean;}Example
// Syntaximport { HashSet, pipe } from "effect"
const set1 = HashSet.make(0, 1)const set2 = HashSet.make(1, 2)const set3 = HashSet.make(0, 1, 2)
// with `data-last`, a.k.a. `pipeable` APIpipe(set1, HashSet.isSubset(set2)) // falsepipe(set1, HashSet.isSubset(set3)) // true
// or piped with the pipe functionset1.pipe(HashSet.isSubset(set2)) // falseset1.pipe(HashSet.isSubset(set3)) // true
// or with `data-first` APIHashSet.isSubset(set1, set2) // falseHashSet.isSubset(set1, set3) // true)Check if a predicate holds true for some HashSet element.
Time complexity: O(n) where n is the number of elements in the set
See
- Other
HashSetelements are :HashSet.has :HashSet.every :HashSet.isSubset
Signature
declare const some: { <A>(f: Predicate<A>): (self: HashSet<A>) => boolean; <A>(self: HashSet<A>, f: Predicate<A>): boolean;}Example
// Syntaximport { HashSet, pipe } from "effect"
const set: HashSet.HashSet<number> = HashSet.make(0, 1, 2)
// with `data-last`, a.k.a. `pipeable` APIpipe( set, HashSet.some((n) => n > 0)) // true
// or piped with the pipe functionset.pipe(HashSet.some((n) => n > 0)) // true
// or with `data-first` APIHashSet.some(set, (n) => n > 0) // trueFiltering
Filters values out of a HashSet using the specified predicate.
The time complexity is of O(n).
Signature
declare const filter: { <A, B>(refinement: Refinement<NoInfer<A>, B>): (self: HashSet<A>) => HashSet<B>; <A>(predicate: Predicate<NoInfer<A>>): (self: HashSet<A>) => HashSet<A>; <A, B>(self: HashSet<A>, refinement: Refinement<A, B>): HashSet<B>; <A>(self: HashSet<A>, predicate: Predicate<A>): HashSet<A>;}Example
// Syntax with Predicateimport { HashSet, type Predicate, pipe } from "effect"
const filterPositiveNumbers: Predicate.Predicate<number> = (n) => n > 0
// with `data-last`, a.k.a. `pipeable` APIpipe( HashSet.make(-2, -1, 0, 1, 2), HashSet.filter(filterPositiveNumbers))
// or with the pipe methodHashSet.make(-2, -1, 0, 1, 2).pipe(HashSet.filter(filterPositiveNumbers))
// or with `data-first` APIHashSet.filter(HashSet.make(-2, -1, 0, 1, 2), filterPositiveNumbers)Example
/// Syntax with Refinementimport { HashSet, pipe } from "effect"
const stringRefinement = (value: unknown): value is string => typeof value === "string"
// with `data-last`, a.k.a. `pipeable` APIpipe( HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), // // HashSet.HashSet<number | string> HashSet.filter(stringRefinement)) // HashSet.HashSet<string>
// or with the pipe methodHashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier") // HashSet.HashSet<number | string> .pipe(HashSet.filter(stringRefinement)) // HashSet.HashSet<string>
// or with `data-first` APIHashSet.filter( HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), // HashSet.HashSet<number | string> stringRefinement) // HashSet.HashSet<string>Folding
Reduces the specified state over the values of the HashSet.
The time complexity is of O(n).
Signature
declare const reduce: { <A, Z>(zero: Z, f: (accumulator: Z, value: A) => Z): (self: HashSet<A>) => Z; <A, Z>(self: HashSet<A>, zero: Z, f: (accumulator: Z, value: A) => Z): Z;}Example
// Syntaximport { HashSet, pipe } from "effect"
const sum = (a: number, b: number): number => a + b
// with `data-last`, a.k.a. `pipeable` APIpipe(HashSet.make(0, 1, 2), HashSet.reduce(0, sum))
// or with the pipe methodHashSet.make(0, 1, 2).pipe(HashSet.reduce(0, sum))
// or with `data-first` APIHashSet.reduce(HashSet.make(0, 1, 2), 0, sum)Getters
Calculates the number of values in the HashSet.
Time complexity: O(1)
See
- Other
HashSetgetters are :HashSet.values :HashSet.toValues
Signature
declare const size: <A>(self: HashSet<A>) => numberExample
import { HashSet, pipe } from "effect"import assert from "node:assert/strict"
assert.deepStrictEqual(pipe(HashSet.empty(), HashSet.size), 0)
assert.deepStrictEqual( pipe(HashSet.make(1, 2, 2, 3, 4, 3), HashSet.size), 4)Returns an Array of the values within the HashSet.
Time complexity: O(n) where n is the number of elements in the set
See
- Other
HashSetgetters are :HashSet.values :HashSet.size
Signature
declare function toValues<A>(self: HashSet<A>): Array<A>Example
import { HashSet, pipe } from "effect"import { deepStrictEqual } from "node:assert/strict"
deepStrictEqual( pipe( HashSet.make(0, 1, 1, 2), // HashSet<number> HashSet.toValues // takes an HashSet<A> and returns an Array<A> ), Array.of(0, 1, 2))Returns an IterableIterator of the values in the HashSet.
Time complexity: O(1)
See
- Other
HashSetgetters are :HashSet.toValues :HashSet.size
Signature
declare const values: <A>(self: HashSet<A>) => IterableIterator<A>Example
import { HashSet, pipe } from "effect"
const numberIterable = pipe( HashSet.make(0, 1, 1, 2), // HashSet.HashSet<number> HashSet.values // takes an HashSet<A> and returns an IterableIterator<A>)
for (const number of numberIterable) { console.log(number) // it will logs: 0, 1, 2}Mapping
Maps over the values of the HashSet using the specified function.
The time complexity is of O(n).
Signature
declare const map: { <A, B>(f: (a: A) => B): (self: HashSet<A>) => HashSet<B>; <A, B>(self: HashSet<A>, f: (a: A) => B): HashSet<B>;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` APIpipe( HashSet.make(0, 1, 2), // HashSet.HashSet<number> HashSet.map(String) // HashSet.HashSet<string>)
// or piped with the pipe methodHashSet.make(0, 1, 2).pipe(HashSet.map(String))
// or with `data-first` APIHashSet.map(HashSet.make(0, 1, 2), String)Models
Other
Adds a value to the HashSet.
Time complexity: O(1) average
See
- Other
HashSetmutations are :HashSet.remove :HashSet.toggle :HashSet.beginMutation :HashSet.endMutation :HashSet.mutate
Signature
declare const add: { <A>(value: A): (self: HashSet<A>) => HashSet<A>; <A>(self: HashSet<A>, value: A): HashSet<A>;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with data-last, a.k.a. pipeable APIpipe(HashSet.empty(), HashSet.add(0), HashSet.add(0))
// or piped with the pipe functionHashSet.empty().pipe(HashSet.add(0))
// or with data-first APIHashSet.add(HashSet.empty(), 0)beginMutation
Creates a new mutable version of the HashSet
When a HashSet is mutable, operations like add and remove
modify the data structure in place instead of creating a new one, which is
more efficient when performing multiple operations.
See
- Other
HashSetmutations are :HashSet.add :HashSet.remove :HashSet.toggle :HashSet.endMutation :HashSet.mutate
Signature
declare const beginMutation: <A>(self: HashSet<A>) => HashSet<A>Example
import { HashSet } from "effect"import assert from "node:assert/strict"
const UPPER_BOUND = 10_000
const immutableSet = HashSet.empty<number>().pipe(HashSet.add(0))
// Create a mutable version of the immutableSetconst mutableSet = HashSet.beginMutation(immutableSet)
for (let i = 1; i < UPPER_BOUND; i++) { // Operations now modify the set in place instead of creating new instances // This is more efficient when making multiple changes const pointerToMutableSet = HashSet.add(mutableSet, i)
// the two sets have the same identity, hence `add` is mutating mutableSet and not returning a new HashSet instance assert(Object.is(mutableSet, pointerToMutableSet)) assert.equal(HashSet.has(mutableSet, i), true) // `i` is in the mutableSet assert.equal(HashSet.has(immutableSet, i), false) // `i` is not in the immutableSet}
const next = UPPER_BOUND + 1// When done, mark the set as immutable againHashSet.endMutation(mutableSet).pipe( HashSet.add(next) // since this returns a new HashSet, it will not be logged as part of the mutableSet)assert.equal(HashSet.has(mutableSet, next), false)
console.log(HashSet.toValues(immutableSet)) // [0]console.log(HashSet.toValues(mutableSet).sort((a, b) => a - b)) // [0, 1, 2, 3, ...rest]difference
Computes the set difference (A - B) between this HashSet and the
specified Iterable<A>.
Time complexity: O(n) where n is the number of elements in the set
NOTE: the hash and equal of the values in both the set and the iterable
must be the same; meaning we cannot compute a difference between a HashSet of bananas and a HashSet of elephants as they are not the same type and
won't implement the Equal trait in the same way.
See
- Other
HashSetoperations are :HashSet.intersection :HashSet.union
Signature
declare const difference: { <A>(that: Iterable<A>): (self: HashSet<A>) => HashSet<A>; <A>(self: HashSet<A>, that: Iterable<A>): HashSet<A>;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with data-last, a.k.a. pipeable APIpipe(HashSet.make(1, 2, 3), HashSet.difference(HashSet.make(3, 4, 5)))
// or piped with the pipe functionHashSet.make(1, 2, 3).pipe(HashSet.difference(HashSet.make(3, 4, 5)))
// or with data-first APIHashSet.difference(HashSet.make(1, 2, 3), HashSet.make(3, 4, 5))endMutation
Makes the HashSet immutable again.
After calling endMutation, operations like add and remove
will create new instances of the HashSet instead of modifying the existing
one.
See
- Other
HashSetmutations are :HashSet.add :HashSet.remove :HashSet.toggle :HashSet.beginMutation :HashSet.mutate
Signature
declare const endMutation: <A>(self: HashSet<A>) => HashSet<A>Example
import { HashSet } from "effect"import assert from "node:assert/strict"
// Create a mutable setconst mutableSet = HashSet.beginMutation(HashSet.empty<number>())
// Add some elements to the mutable setHashSet.add(mutableSet, 1)HashSet.add(mutableSet, 2)
// Before endMutation, operations modify the set in placeconst sameSet = HashSet.add(mutableSet, 3)assert(Object.is(mutableSet, sameSet)) // true - same object referenceassert.deepStrictEqual(HashSet.toValues(mutableSet).sort(), [1, 2, 3])
// Make the set immutable againconst immutableSet = HashSet.endMutation(mutableSet)
// endMutation returns the same set instance, now made immutableassert(Object.is(mutableSet, immutableSet)) // true - same object reference
// After endMutation, operations create new instancesconst newSet = HashSet.add(immutableSet, 4)assert(!Object.is(immutableSet, newSet)) // false - different object references
// The original set remains unchangedassert.deepStrictEqual(HashSet.toValues(immutableSet).sort(), [1, 2, 3])
// The new set contains the added elementassert.deepStrictEqual(HashSet.toValues(newSet).sort(), [1, 2, 3, 4])intersection
Returns a HashSet of values which are present in both this set and that
Iterable<A>. Computes set intersection (A ∩ B)
Time complexity: O(n) where n is the number of elements in the smaller
set
NOTE: the hash and equal of the values in both the set and the iterable must be the same.
See
- Other
HashSetoperations are :HashSet.difference :HashSet.union
Signature
declare const intersection: { <A>(that: Iterable<A>): (self: HashSet<A>) => HashSet<A>; <A>(self: HashSet<A>, that: Iterable<A>): HashSet<A>;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with data-last, a.k.a. pipeable APIpipe(HashSet.make(1, 2, 3), HashSet.intersection(HashSet.make(2, 3, 4)))
// or piped with the pipe functionHashSet.make(1, 2, 3).pipe(HashSet.intersection(HashSet.make(2, 3, 4)))
// or with data-first APIHashSet.intersection(HashSet.make(1, 2, 3), HashSet.make(2, 3, 4))Mutates the HashSet within the context of the provided function.
You can consider it a functional abstraction on top of the lower-level
mutation primitives of :HashSet.beginMutation -> mutable context -> HashSet.endMutation.
See
- Other
HashSetmutations are :HashSet.add :HashSet.remove :HashSet.toggle :HashSet.beginMutation :HashSet.endMutation
Signature
declare const mutate: { <A>(f: (set: HashSet<A>) => void): (self: HashSet<A>) => HashSet<A>; <A>(self: HashSet<A>, f: (set: HashSet<A>) => void): HashSet<A>;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with data-last, a.k.a. pipeable APIpipe( HashSet.make(1, 2, 3), HashSet.mutate((set) => { HashSet.add(set, 4) HashSet.remove(set, 1) }))
// or piped with the pipe functionHashSet.make(1, 2, 3).pipe( HashSet.mutate((set) => { HashSet.add(set, 4) HashSet.remove(set, 1) }))
// or with data-first APIHashSet.mutate(HashSet.make(1, 2, 3), (set) => { HashSet.add(set, 4) HashSet.remove(set, 1)})Removes a value from the HashSet.
Time complexity: O(1) average
See
- Other
HashSetmutations are :HashSet.add :HashSet.toggle :HashSet.beginMutation :HashSet.endMutation :HashSet.mutate
Signature
declare const remove: { <A>(value: A): (self: HashSet<A>) => HashSet<A>; <A>(self: HashSet<A>, value: A): HashSet<A>;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` APIpipe(HashSet.make(0, 1, 2), HashSet.remove(0))
// or piped with the pipe functionHashSet.make(0, 1, 2).pipe(HashSet.remove(0))
// or with `data-first` APIHashSet.remove(HashSet.make(0, 1, 2), 0)Checks if a value is present in the HashSet. If it is present, the value
will be removed from the HashSet, otherwise the value will be added to the
HashSet.
Time complexity: O(1) average
See
- Other
HashSetmutations are :HashSet.add :HashSet.remove :HashSet.beginMutation :HashSet.endMutation :HashSet.mutate
Signature
declare const toggle: { <A>(value: A): (self: HashSet<A>) => HashSet<A>; <A>(self: HashSet<A>, value: A): HashSet<A>;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` APIpipe(HashSet.make(0, 1, 2), HashSet.toggle(0))
// or piped with the pipe functionHashSet.make(0, 1, 2).pipe(HashSet.toggle(0))
// or with `data-first` APIHashSet.toggle(HashSet.make(0, 1, 2), 0)Computes the set union ( self ∪ that ) between this HashSet and the
specified Iterable<A>.
Time complexity: O(n) where n is the number of elements in the set
NOTE: the hash and equal of the values in both the set and the iterable must be the same.
See
- Other
HashSetoperations are :HashSet.difference :HashSet.intersection
Signature
declare const union: { <A>(that: Iterable<A>): (self: HashSet<A>) => HashSet<A>; <A>(self: HashSet<A>, that: Iterable<A>): HashSet<A>;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with data-last, a.k.a. pipeable APIpipe(HashSet.make(1, 2, 3), HashSet.union(HashSet.make(3, 4, 5)))
// or piped with the pipe functionHashSet.make(1, 2, 3).pipe(HashSet.union(HashSet.make(3, 4, 5)))
// or with data-first APIHashSet.union(HashSet.make(1, 2, 3), HashSet.make(3, 4, 5))Partitioning
Partition the values of a HashSet using the specified predicate.
If a value matches the predicate, it will be placed into the HashSet on the
right side of the resulting Tuple, otherwise the value will be placed into
the left side.
Time complexity is of O(n).
Signature
declare const partition: { <A, B>(refinement: Refinement<NoInfer<A>, B>): (self: HashSet<A>) => [excluded: HashSet<Exclude<A, B>>, satisfying: HashSet<B>]; <A>(predicate: Predicate<NoInfer<A>>): (self: HashSet<A>) => [excluded: HashSet<A>, satisfying: HashSet<A>]; <A, B>(self: HashSet<A>, refinement: Refinement<A, B>): [excluded: HashSet<Exclude<A, B>>, satisfying: HashSet<B>]; <A>(self: HashSet<A>, predicate: Predicate<A>): [excluded: HashSet<A>, satisfying: HashSet<A>];}Example
// Syntax with Predicateimport { HashSet, pipe, Predicate } from "effect"
// with `data-last`, a.k.a. `pipeable` APIpipe( HashSet.make(0, 1, 2, 3, 4, 5), HashSet.partition((n) => n % 2 === 0))
// or with the pipe methodHashSet.make(0, 1, 2, 3, 4, 5).pipe( HashSet.partition((n) => n % 2 === 0))
// or with `data-first` APIHashSet.partition(HashSet.make(0, 1, 2, 3, 4, 5), (n) => n % 2 === 0)Example
// Syntax with Refinementimport { HashSet, pipe, Predicate } from "effect"
const stringRefinement: Predicate.Refinement<string | number, string> = ( value) => typeof value === "string"
// with `data-last`, a.k.a. `pipeable` APIpipe( HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), HashSet.partition(stringRefinement))
// or with the pipe methodHashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier").pipe( HashSet.partition(stringRefinement))
// or with `data-first` APIHashSet.partition( HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), stringRefinement)Refinements
Sequencing
Chains over the values of the HashSet using the specified function.
The time complexity is of O(n).
Signature
declare const flatMap: { <A, B>(f: (a: A) => Iterable<B>): (self: HashSet<A>) => HashSet<B>; <A, B>(self: HashSet<A>, f: (a: A) => Iterable<B>): HashSet<B>;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` APIpipe( HashSet.make(0, 1, 2), // HashSet.HashSet<number> HashSet.flatMap((n) => Array.of(String(n))) // HashSet.HashSet<string>)
// or piped with the pipe methodHashSet.make(0, 1, 2) // HashSet.HashSet<number> .pipe( HashSet.flatMap((n) => Array.of(String(n))) // HashSet.HashSet<string> )
// or with `data-first` APIHashSet.flatMap(HashSet.make(0, 1, 2), (n) => Array.of(String(n)))Symbol
Traversing
Applies the specified function to the values of the HashSet.
The time complexity is of O(n).
Signature
declare const forEach: { <A>(f: (value: A) => void): (self: HashSet<A>) => void; <A>(self: HashSet<A>, f: (value: A) => void): void;}Example
// Syntaximport { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` APIpipe(HashSet.make(0, 1, 2), HashSet.forEach(console.log)) // logs: 0 1 2
// or piped with the pipe methodHashSet.make(0, 1, 2).pipe(HashSet.forEach(console.log)) // logs: 0 1 2
// or with `data-first` APIHashSet.forEach(HashSet.make(0, 1, 2), console.log) // logs: 0 1 2