HashSet.ts overview
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 (
module:HashSet.has
):O(1)
average time complexity - Insertion operations (
module:HashSet.add
):O(1)
average time complexity - Removal operations (
module:HashSet.remove
):O(1)
average time complexity - Set operations (
module:HashSet.union
,module: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 setEqual
: allows comparing two sets for value-based equalityPipeable
: allows chaining operations with the pipe operatorInspectable
: allows inspecting the contents of the set
Operations Reference
Category | Operation | Description | Complexity |
---|---|---|---|
constructors | module:HashSet.empty | Creates an empty HashSet | O(1) |
constructors | module:HashSet.fromIterable | Creates a HashSet from an iterable | O(n) |
constructors | module:HashSet.make | Creates a HashSet from multiple values | O(n) |
elements | module:HashSet.has | Checks if a value exists in the set | O(1) avg |
elements | module:HashSet.some | Checks if any element satisfies a predicate | O(n) |
elements | module:HashSet.every | Checks if all elements satisfy a predicate | O(n) |
elements | module:HashSet.isSubset | Checks if a set is a subset of another | O(n) |
getters | module:HashSet.values | Gets an iterator of all values | O(1) |
getters | module:HashSet.toValues | Gets an array of all values | O(n) |
getters | module:HashSet.size | Gets the number of elements | O(1) |
mutations | module:HashSet.add | Adds a value to the set | O(1) avg |
mutations | module:HashSet.remove | Removes a value from the set | O(1) avg |
mutations | module:HashSet.toggle | Toggles a value’s presence | O(1) avg |
operations | module:HashSet.difference | Computes set difference (A - B) | O(n) |
operations | module:HashSet.intersection | Computes set intersection (A ∩ B) | O(n) |
operations | module:HashSet.union | Computes set union (A ∪ B) | O(n) |
mapping | module:HashSet.map | Transforms each element | O(n) |
sequencing | module:HashSet.flatMap | Transforms and flattens elements | O(n) |
traversing | module:HashSet.forEach | Applies a function to each element | O(n) |
folding | module:HashSet.reduce | Reduces the set to a single value | O(n) |
filtering | module:HashSet.filter | Keeps elements that satisfy a predicate | O(n) |
partitioning | module: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 implementsEqual
, theHashSet
will 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.
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 interface
const 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 stored
console.log(HashSet.size(set))
// Output: 1
Simplifying Equality and Hashing with Data
and Schema
:
Effect’s Data
and Schema.Data
modules offer powerful ways to automatically handle the implementation of both the Equal
and Hash
traits for your custom data structures.
Data
Module: By using constructors likeData.struct
,Data.tuple
,Data.array
, orData.case
to define your data types, Effect automatically generates the necessary implementations for value-based equality and consistent hashing. This significantly reduces boilerplate and ensures correctness.
import { HashSet, Data, Equal } from "effect"
import assert from "node:assert/strict"
// Data.* implements the `Equal` traits for us
const 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 stored
console.log(HashSet.size(set)) // Output: 1
Schema
Module: When defining data schemas using theSchema
module, you can useSchema.Data
to automatically include theEqual
andHash
traits 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
.
import { Equal, HashSet, Schema } from "effect"
import assert from "node:assert/strict"
// Schema.Data implements the `Equal` traits for us
const 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 stored
console.log(HashSet.size(set)) // Output: 1
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
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 ]
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.
Since v2.0.0
Exports Grouped by Category
- constructors
- elements
- filtering
- folding
- getters
- mapping
- models
- partitioning
- refinements
- sequencing
- symbol
- traversing
- utils
constructors
empty
Creates an empty HashSet
.
Time complexity: O(1)
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]
See
- Other
HashSet
constructors aremodule:HashSet.make
module:HashSet.fromIterable
Signature
declare const empty: <A = never>() => HashSet<A>
Since v2.0.0
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
Example
// Creating a HashSet from an Array
import { 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 Set
import { 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 Generator
import { HashSet } from "effect"
// Generator functions return iterables
function* 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 numbers
const fibonacciSet = HashSet.fromIterable(fibonacci(10))
console.log(HashSet.toValues(fibonacciSet))
// Outputs: [0, 1, 2, 3, 5, 8, 13, 21, 34] but in unsorted order
Example
// Creating a HashSet from another HashSet
import { 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 Chunk
import { 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]
See
- Other
HashSet
constructors aremodule:HashSet.empty
module:HashSet.make
Signature
declare const fromIterable: <A>(elements: Iterable<A>) => HashSet<A>
Since v2.0.0
make
Construct a new HashSet
from a variable number of values.
Time complexity: O(n)
where n is the number of elements
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"
)
See
- Other
HashSet
constructors aremodule:HashSet.fromIterable
module:HashSet.empty
Signature
declare const make: <As extends ReadonlyArray<any>>(...elements: As) => HashSet<As[number]>
Since v2.0.0
elements
every
Check if a predicate holds true for every HashSet
element.
Time complexity is O(n)
as it needs to traverse the whole HashSet collection
Example
// Syntax with Refinement
import { 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 Predicate
import { HashSet, pipe } from "effect"
const set = HashSet.make(1, 2, 3)
// with `data-last`, a.k.a. `pipeable` API
pipe(
set,
HashSet.every((n) => n >= 0)
) // true
// or piped with the pipe function
set.pipe(HashSet.every((n) => n >= 0)) // true
// or with `data-first` API
HashSet.every(set, (n) => n >= 0) // true
See
- Other
HashSet
elements aremodule:HashSet.has
module:HashSet.some
module:HashSet.isSubset
Signature
declare const every: {
<A, B extends A>(refinement: Refinement<NoInfer<A>, B>): (self: HashSet<A>) => self is HashSet<B>
<A>(predicate: Predicate<A>): (self: HashSet<A>) => boolean
<A, B extends A>(self: HashSet<A>, refinement: Refinement<A, B>): self is HashSet<B>
<A>(self: HashSet<A>, predicate: Predicate<A>): boolean
}
Since v2.0.0
has
Checks if the specified value exists in the HashSet
.
Time complexity: O(1)
average
Example
// Syntax
import { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` API
pipe(HashSet.make(0, 1, 2), HashSet.has(3)) // false
// or piped with the pipe function
HashSet.make(0, 1, 2).pipe(HashSet.has(3)) // false
// or with `data-first` API
HashSet.has(HashSet.make(0, 1, 2), 3) // false
See
- Other
HashSet
elements aremodule:HashSet.some
module:HashSet.every
module:HashSet.isSubset
Signature
declare const has: { <A>(value: A): (self: HashSet<A>) => boolean; <A>(self: HashSet<A>, value: A): boolean }
Since v2.0.0
isSubset
Returns 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)
Example
// Syntax
import { 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` API
pipe(set1, HashSet.isSubset(set2)) // false
pipe(set1, HashSet.isSubset(set3)) // true
// or piped with the pipe function
set1.pipe(HashSet.isSubset(set2)) // false
set1.pipe(HashSet.isSubset(set3)) // true
// or with `data-first` API
HashSet.isSubset(set1, set2) // false
HashSet.isSubset(set1, set3) // true)
See
- Other
HashSet
elements aremodule:HashSet.has
module:HashSet.some
module:HashSet.every
Signature
declare const isSubset: {
<A>(that: HashSet<A>): (self: HashSet<A>) => boolean
<A>(self: HashSet<A>, that: HashSet<A>): boolean
}
Since v2.0.0
some
Check if a predicate holds true for some HashSet
element.
Time complexity: O(n)
where n is the number of elements in the set
Example
// Syntax
import { HashSet, pipe } from "effect"
const set: HashSet.HashSet<number> = HashSet.make(0, 1, 2)
// with `data-last`, a.k.a. `pipeable` API
pipe(
set,
HashSet.some((n) => n > 0)
) // true
// or piped with the pipe function
set.pipe(HashSet.some((n) => n > 0)) // true
// or with `data-first` API
HashSet.some(set, (n) => n > 0) // true
See
- Other
HashSet
elements aremodule:HashSet.has
module:HashSet.every
module:HashSet.isSubset
Signature
declare const some: {
<A>(f: Predicate<A>): (self: HashSet<A>) => boolean
<A>(self: HashSet<A>, f: Predicate<A>): boolean
}
Since v2.0.0
filtering
filter
Filters values out of a HashSet
using the specified predicate.
The time complexity is of O(n)
.
Example
// Syntax with Predicate
import { HashSet, type Predicate, pipe } from "effect"
const filterPositiveNumbers: Predicate.Predicate<number> = (n) => n > 0
// with `data-last`, a.k.a. `pipeable` API
pipe(HashSet.make(-2, -1, 0, 1, 2), HashSet.filter(filterPositiveNumbers))
// or with the pipe method
HashSet.make(-2, -1, 0, 1, 2).pipe(HashSet.filter(filterPositiveNumbers))
// or with `data-first` API
HashSet.filter(HashSet.make(-2, -1, 0, 1, 2), filterPositiveNumbers)
Example
/// Syntax with Refinement
import { HashSet, pipe } from "effect"
const stringRefinement = (value: unknown): value is string => typeof value === "string"
// with `data-last`, a.k.a. `pipeable` API
pipe(
HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), // // HashSet.HashSet<number | string>
HashSet.filter(stringRefinement)
) // HashSet.HashSet<string>
// or with the pipe method
HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier") // HashSet.HashSet<number | string>
.pipe(HashSet.filter(stringRefinement)) // HashSet.HashSet<string>
// or with `data-first` API
HashSet.filter(
HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), // HashSet.HashSet<number | string>
stringRefinement
) // HashSet.HashSet<string>
Signature
declare const filter: {
<A, B extends A>(refinement: Refinement<NoInfer<A>, B>): (self: HashSet<A>) => HashSet<B>
<A>(predicate: Predicate<NoInfer<A>>): (self: HashSet<A>) => HashSet<A>
<A, B extends A>(self: HashSet<A>, refinement: Refinement<A, B>): HashSet<B>
<A>(self: HashSet<A>, predicate: Predicate<A>): HashSet<A>
}
Since v2.0.0
folding
reduce
Reduces the specified state over the values of the HashSet
.
The time complexity is of O(n)
.
Example
// Syntax
import { HashSet, pipe } from "effect"
const sum = (a: number, b: number): number => a + b
// with `data-last`, a.k.a. `pipeable` API
pipe(HashSet.make(0, 1, 2), HashSet.reduce(0, sum))
// or with the pipe method
HashSet.make(0, 1, 2).pipe(HashSet.reduce(0, sum))
// or with `data-first` API
HashSet.reduce(HashSet.make(0, 1, 2), 0, sum)
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
}
Since v2.0.0
getters
size
Calculates the number of values in the HashSet
.
Time complexity: O(1)
Example
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)
See
- Other
HashSet
getters aremodule:HashSet.values
module:HashSet.toValues
Signature
declare const size: <A>(self: HashSet<A>) => number
Since v2.0.0
toValues
Returns an Array
of the values within the HashSet
.
Time complexity: O(n)
where n is the number of elements in the set
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)
)
See
- Other
HashSet
getters aremodule:HashSet.values
module:HashSet.size
Signature
declare const toValues: <A>(self: HashSet<A>) => Array<A>
Since v3.13.0
values
Returns an IterableIterator
of the values in the HashSet
.
Time complexity: O(1)
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
}
See
- Other
HashSet
getters aremodule:HashSet.toValues
module:HashSet.size
Signature
declare const values: <A>(self: HashSet<A>) => IterableIterator<A>
Since v2.0.0
mapping
map
Maps over the values of the HashSet
using the specified function.
The time complexity is of O(n)
.
Example
// Syntax
import { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` API
pipe(
HashSet.make(0, 1, 2), // HashSet.HashSet<number>
HashSet.map(String) // HashSet.HashSet<string>
)
// or piped with the pipe method
HashSet.make(0, 1, 2).pipe(HashSet.map(String))
// or with `data-first` API
HashSet.map(HashSet.make(0, 1, 2), String)
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>
}
Since v2.0.0
models
HashSet (interface)
Example
// Syntax
import { HashSet } from "effect"
let numberSet: HashSet.HashSet<number>
Signature
export interface HashSet<out A> extends Iterable<A>, Equal, Pipeable, Inspectable {
readonly [TypeId]: TypeId
}
Since v2.0.0
partitioning
partition
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)
.
Example
// Syntax with Predicate
import { HashSet, pipe, Predicate } from "effect"
// with `data-last`, a.k.a. `pipeable` API
pipe(
HashSet.make(0, 1, 2, 3, 4, 5),
HashSet.partition((n) => n % 2 === 0)
)
// or with the pipe method
HashSet.make(0, 1, 2, 3, 4, 5).pipe(HashSet.partition((n) => n % 2 === 0))
// or with `data-first` API
HashSet.partition(HashSet.make(0, 1, 2, 3, 4, 5), (n) => n % 2 === 0)
Example
// Syntax with Refinement
import { HashSet, pipe, Predicate } from "effect"
const stringRefinement: Predicate.Refinement<string | number, string> = (value) => typeof value === "string"
// with `data-last`, a.k.a. `pipeable` API
pipe(HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), HashSet.partition(stringRefinement))
// or with the pipe method
HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier").pipe(HashSet.partition(stringRefinement))
// or with `data-first` API
HashSet.partition(HashSet.make(1, "unos", 2, "two", 3, "trois", 4, "vier"), stringRefinement)
Signature
declare const partition: {
<A, B extends A>(
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 extends A>(
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>]
}
Since v2.0.0
refinements
isHashSet
Signature
declare const isHashSet: { <A>(u: Iterable<A>): u is HashSet<A>; (u: unknown): u is HashSet<unknown> }
Since v2.0.0
sequencing
flatMap
Chains over the values of the HashSet
using the specified function.
The time complexity is of O(n)
.
Example
// Syntax
import { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` API
pipe(
HashSet.make(0, 1, 2), // HashSet.HashSet<number>
HashSet.flatMap((n) => Array.of(String(n))) // HashSet.HashSet<string>
)
// or piped with the pipe method
HashSet.make(0, 1, 2) // HashSet.HashSet<number>
.pipe(
HashSet.flatMap((n) => Array.of(String(n))) // HashSet.HashSet<string>
)
// or with `data-first` API
HashSet.flatMap(HashSet.make(0, 1, 2), (n) => Array.of(String(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>
}
Since v2.0.0
symbol
TypeId (type alias)
Signature
type TypeId = typeof TypeId
Since v2.0.0
traversing
forEach
Applies the specified function to the values of the HashSet
.
The time complexity is of O(n)
.
Example
// Syntax
import { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` API
pipe(HashSet.make(0, 1, 2), HashSet.forEach(console.log)) // logs: 0 1 2
// or piped with the pipe method
HashSet.make(0, 1, 2).pipe(HashSet.forEach(console.log)) // logs: 0 1 2
// or with `data-first` API
HashSet.forEach(HashSet.make(0, 1, 2), console.log) // logs: 0 1 2
Signature
declare const forEach: {
<A>(f: (value: A) => void): (self: HashSet<A>) => void
<A>(self: HashSet<A>, f: (value: A) => void): void
}
Since v2.0.0
utils
add
Adds a value to the HashSet
.
Time complexity: O(1)
average
Example
// Syntax
import { HashSet, pipe } from "effect"
// with data-last, a.k.a. pipeable API
pipe(HashSet.empty(), HashSet.add(0), HashSet.add(0))
// or piped with the pipe function
HashSet.empty().pipe(HashSet.add(0))
// or with data-first API
HashSet.add(HashSet.empty(), 0)
See
- Other
HashSet
mutations aremodule:HashSet.remove
module:HashSet.toggle
module:HashSet.beginMutation
module:HashSet.endMutation
module:HashSet.mutate
Signature
declare const add: { <A>(value: A): (self: HashSet<A>) => HashSet<A>; <A>(self: HashSet<A>, value: A): HashSet<A> }
Since v2.0.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.
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 immutableSet
const 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 again
HashSet.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]
See
- Other
HashSet
mutations aremodule:HashSet.add
module:HashSet.remove
module:HashSet.toggle
module:HashSet.endMutation
module:HashSet.mutate
Signature
declare const beginMutation: <A>(self: HashSet<A>) => HashSet<A>
Since v2.0.0
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.
Example
// Syntax
import { HashSet, pipe } from "effect"
// with data-last, a.k.a. pipeable API
pipe(HashSet.make(1, 2, 3), HashSet.difference(HashSet.make(3, 4, 5)))
// or piped with the pipe function
HashSet.make(1, 2, 3).pipe(HashSet.difference(HashSet.make(3, 4, 5)))
// or with data-first API
HashSet.difference(HashSet.make(1, 2, 3), HashSet.make(3, 4, 5))
See
- Other
HashSet
operations aremodule:HashSet.intersection
module:HashSet.union
Signature
declare const difference: {
<A>(that: Iterable<A>): (self: HashSet<A>) => HashSet<A>
<A>(self: HashSet<A>, that: Iterable<A>): HashSet<A>
}
Since v2.0.0
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.
Example
import { HashSet } from "effect"
import assert from "node:assert/strict"
// Create a mutable set
const mutableSet = HashSet.beginMutation(HashSet.empty<number>())
// Add some elements to the mutable set
HashSet.add(mutableSet, 1)
HashSet.add(mutableSet, 2)
// Before endMutation, operations modify the set in place
const sameSet = HashSet.add(mutableSet, 3)
assert(Object.is(mutableSet, sameSet)) // true - same object reference
assert.deepStrictEqual(HashSet.toValues(mutableSet).sort(), [1, 2, 3])
// Make the set immutable again
const immutableSet = HashSet.endMutation(mutableSet)
// endMutation returns the same set instance, now made immutable
assert(Object.is(mutableSet, immutableSet)) // true - same object reference
// After endMutation, operations create new instances
const newSet = HashSet.add(immutableSet, 4)
assert(!Object.is(immutableSet, newSet)) // false - different object references
// The original set remains unchanged
assert.deepStrictEqual(HashSet.toValues(immutableSet).sort(), [1, 2, 3])
// The new set contains the added element
assert.deepStrictEqual(HashSet.toValues(newSet).sort(), [1, 2, 3, 4])
See
- Other
HashSet
mutations aremodule:HashSet.add
module:HashSet.remove
module:HashSet.toggle
module:HashSet.beginMutation
module:HashSet.mutate
Signature
declare const endMutation: <A>(self: HashSet<A>) => HashSet<A>
Since v2.0.0
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.
Example
// Syntax
import { HashSet, pipe } from "effect"
// with data-last, a.k.a. pipeable API
pipe(HashSet.make(1, 2, 3), HashSet.intersection(HashSet.make(2, 3, 4)))
// or piped with the pipe function
HashSet.make(1, 2, 3).pipe(HashSet.intersection(HashSet.make(2, 3, 4)))
// or with data-first API
HashSet.intersection(HashSet.make(1, 2, 3), HashSet.make(2, 3, 4))
See
- Other
HashSet
operations aremodule:HashSet.difference
module:HashSet.union
Signature
declare const intersection: {
<A>(that: Iterable<A>): (self: HashSet<A>) => HashSet<A>
<A>(self: HashSet<A>, that: Iterable<A>): HashSet<A>
}
Since v2.0.0
mutate
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 module:HashSet.beginMutation
->
mutable context
->
HashSet.endMutation
.
Example
// Syntax
import { HashSet, pipe } from "effect"
// with data-last, a.k.a. pipeable API
pipe(
HashSet.make(1, 2, 3),
HashSet.mutate((set) => {
HashSet.add(set, 4)
HashSet.remove(set, 1)
})
)
// or piped with the pipe function
HashSet.make(1, 2, 3).pipe(
HashSet.mutate((set) => {
HashSet.add(set, 4)
HashSet.remove(set, 1)
})
)
// or with data-first API
HashSet.mutate(HashSet.make(1, 2, 3), (set) => {
HashSet.add(set, 4)
HashSet.remove(set, 1)
})
See
- Other
HashSet
mutations aremodule:HashSet.add
module:HashSet.remove
module:HashSet.toggle
module:HashSet.beginMutation
module: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>
}
Since v2.0.0
remove
Removes a value from the HashSet
.
Time complexity: O(1)
average
Example
// Syntax
import { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` API
pipe(HashSet.make(0, 1, 2), HashSet.remove(0))
// or piped with the pipe function
HashSet.make(0, 1, 2).pipe(HashSet.remove(0))
// or with `data-first` API
HashSet.remove(HashSet.make(0, 1, 2), 0)
See
- Other
HashSet
mutations aremodule:HashSet.add
module:HashSet.toggle
module:HashSet.beginMutation
module:HashSet.endMutation
module:HashSet.mutate
Signature
declare const remove: { <A>(value: A): (self: HashSet<A>) => HashSet<A>; <A>(self: HashSet<A>, value: A): HashSet<A> }
Since v2.0.0
toggle
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
Example
// Syntax
import { HashSet, pipe } from "effect"
// with `data-last`, a.k.a. `pipeable` API
pipe(HashSet.make(0, 1, 2), HashSet.toggle(0))
// or piped with the pipe function
HashSet.make(0, 1, 2).pipe(HashSet.toggle(0))
// or with `data-first` API
HashSet.toggle(HashSet.make(0, 1, 2), 0)
See
- Other
HashSet
mutations aremodule:HashSet.add
module:HashSet.remove
module:HashSet.beginMutation
module:HashSet.endMutation
module:HashSet.mutate
Signature
declare const toggle: { <A>(value: A): (self: HashSet<A>) => HashSet<A>; <A>(self: HashSet<A>, value: A): HashSet<A> }
Since v2.0.0
union
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.
Example
// Syntax
import { HashSet, pipe } from "effect"
// with data-last, a.k.a. pipeable API
pipe(HashSet.make(1, 2, 3), HashSet.union(HashSet.make(3, 4, 5)))
// or piped with the pipe function
HashSet.make(1, 2, 3).pipe(HashSet.union(HashSet.make(3, 4, 5)))
// or with data-first API
HashSet.union(HashSet.make(1, 2, 3), HashSet.make(3, 4, 5))
See
- Other
HashSet
operations aremodule:HashSet.difference
module:HashSet.intersection
Signature
declare const union: {
<A>(that: Iterable<A>): (self: HashSet<A>) => HashSet<A>
<A>(self: HashSet<A>, that: Iterable<A>): HashSet<A>
}
Since v2.0.0