README

#Set Package Documentation

This package provides a hash-based set data structure that maintains insertion order. The Set[K] type stores unique elements and provides efficient membership testing, insertion, and deletion operations.

#Creating Sets

There are several ways to create sets:

///|
test "creating sets" {
// Empty set
let empty_set : @set.Set[Int] = Set([])
inspect(empty_set.length(), content="0")
inspect(empty_set.is_empty(), content="true")

// Set with initial capacity
let set_with_capacity : @set.Set[Int] = Set([], capacity=16)
inspect(set_with_capacity.capacity(), content="16")

// From array
let from_array = @set.Set([1, 2, 3, 2, 1]) // Duplicates are removed
inspect(from_array.length(), content="3")

// From fixed array
let from_fixed = @set.Set([10, 20, 30])
inspect(from_fixed.length(), content="3")

// From iterator
let from_iter = @set.Set::from_iter([1, 2, 3, 4, 5].iter())
inspect(from_iter.length(), content="5")
}

#Basic Operations

Add, remove, and check membership:

///|
test "basic operations" {
let set = @set.Set([])

// Adding elements
set.add("apple")
set.add("banana")
set.add("cherry")
inspect(set.length(), content="3")

// Adding duplicate (no effect)
set.add("apple")
inspect(set.length(), content="3") // Still 3

// Check membership
inspect(set.contains("apple"), content="true")
inspect(set.contains("orange"), content="false")

// Remove elements
set.remove("banana")
inspect(set.contains("banana"), content="false")
inspect(set.length(), content="2")

// Check if addition/removal was successful
let was_added = set.add_and_check("date")
inspect(was_added, content="true")
let was_added_again = set.add_and_check("date")
inspect(was_added_again, content="false") // Already exists
let was_removed = set.remove_and_check("cherry")
inspect(was_removed, content="true")
let was_removed_again = set.remove_and_check("cherry")
inspect(was_removed_again, content="false") // Doesn't exist
}

#Set Operations

Perform mathematical set operations:

///|
test "set operations" {
let set1 = @set.Set([1, 2, 3, 4])
let set2 = @set.Set([3, 4, 5, 6])

// Union (all elements from both sets)
let union_set = set1.union(set2)
let union_array = union_set.to_array()
inspect(union_array.length(), content="6") // [1, 2, 3, 4, 5, 6]

// Alternative union syntax
let union_alt = set1 | set2
inspect(union_alt.length(), content="6")

// Intersection (common elements)
let intersection_set = set1.intersection(set2)
let intersection_array = intersection_set.to_array()
inspect(intersection_array.length(), content="2") // [3, 4]

// Alternative intersection syntax
let intersection_alt = set1 & set2
inspect(intersection_alt.length(), content="2")

// Difference (elements in first but not second)
let difference_set = set1.difference(set2)
let difference_array = difference_set.to_array()
inspect(difference_array.length(), content="2") // [1, 2]

// Alternative difference syntax
let difference_alt = set1 - set2
inspect(difference_alt.length(), content="2")

// Symmetric difference (elements in either but not both)
let sym_diff_set = set1.symmetric_difference(set2)
let sym_diff_array = sym_diff_set.to_array()
inspect(sym_diff_array.length(), content="4") // [1, 2, 5, 6]

// Alternative symmetric difference syntax
let sym_diff_alt = set1 ^ set2
inspect(sym_diff_alt.length(), content="4")
}

#Set Relationships

Test relationships between sets:

///|
test "set relationships" {
let small_set = @set.Set([1, 2])
let large_set = @set.Set([1, 2, 3, 4])
let disjoint_set = @set.Set([5, 6, 7])

// Subset testing
inspect(small_set.is_subset(large_set), content="true")
inspect(large_set.is_subset(small_set), content="false")

// Superset testing
inspect(large_set.is_superset(small_set), content="true")
inspect(small_set.is_superset(large_set), content="false")

// Disjoint testing (no common elements)
inspect(small_set.is_disjoint(disjoint_set), content="true")
inspect(small_set.is_disjoint(large_set), content="false")

// Equal sets
let set1 = @set.Set([1, 2, 3])
let set2 = @set.Set([3, 2, 1]) // Order doesn't matter
inspect(set1 == set2, content="true")
}

#Iteration and Conversion

Iterate over sets and convert to other types:

///|
test "iteration and conversion" {
let set = @set.Set(["first", "second", "third"])

// Convert to array (maintains insertion order)
let array = set.to_array()
inspect(array.length(), content="3")

// Iterate over elements
let mut count = 0
set.each(fn(_element) { count = count + 1 })
inspect(count, content="3")

// Iterate with index
let mut indices_sum = 0
set.eachi(fn(i, _element) { indices_sum = indices_sum + i })
inspect(indices_sum, content="3") // 0 + 1 + 2 = 3

// Use iterator
let elements = set.iter().collect()
inspect(elements.length(), content="3")

// Copy a set
let copied_set = set.copy()
inspect(copied_set.length(), content="3")
inspect(copied_set == set, content="true")
}

#Modifying Sets

Clear and modify existing sets:

///|
test "modifying sets" {
let set = @set.Set([10, 20, 30, 40, 50])
inspect(set.length(), content="5")

// Clear all elements
set.clear()
inspect(set.length(), content="0")
inspect(set.is_empty(), content="true")

// Add elements back
set.add(100)
set.add(200)
inspect(set.length(), content="2")
inspect(set.contains(100), content="true")
}

#JSON Serialization

Sets can be serialized to JSON as arrays:

///|
test "json serialization" {
let set = @set.Set([1, 2, 3])
let json = @json.to_json(set)

// JSON representation is an array
@debug.debug_inspect(json, content="Array([Number(1), Number(2), Number(3)])")

// String set
let string_set = @set.Set(["a", "b", "c"])
let string_json = @json.to_json(string_set)
@debug.debug_inspect(
string_json,
content="Array([String(\"a\"), String(\"b\"), String(\"c\")])",
)
}

#Working with Different Types

Sets work with any type that implements Hash and Eq:

///|
test "different types" {
// Integer set
let int_set = @set.Set([1, 2, 3, 4, 5])
inspect(int_set.contains(3), content="true")

// String set
let string_set = @set.Set(["hello", "world", "moonbit"])
inspect(string_set.contains("world"), content="true")

// Note: Char and Bool types don't implement Hash in this version
// So we use Int codes for demonstration
let char_codes = @set.Set([97, 98, 99]) // ASCII codes for 'a', 'b', 'c'
inspect(char_codes.contains(98), content="true") // 'b' = 98

// Integer set representing boolean values
let bool_codes = @set.Set([1, 0, 1]) // 1=true, 0=false
inspect(bool_codes.length(), content="2") // Only 1 and 0
}

#Performance Examples

Demonstrate efficient operations:

///|
test "performance examples" {
// Large set operations
let large_set = @set.Set([], capacity=1000)

// Add many elements
for i in 0..<100 {
large_set.add(i)
}
inspect(large_set.length(), content="100")

// Fast membership testing
inspect(large_set.contains(50), content="true")
inspect(large_set.contains(150), content="false")

// Efficient set operations on large sets
let another_set = @set.Set([])
for i in 50..<150 {
another_set.add(i)
}
let intersection = large_set.intersection(another_set)
inspect(intersection.length(), content="50") // Elements 50-99
}

#Use Cases

Sets are particularly useful for:

  1. Removing duplicates: Convert arrays to sets and back to remove duplicates
  2. Membership testing: Fast O(1) average-case lookups
  3. Mathematical operations: Union, intersection, difference operations
  4. Unique collections: Maintaining collections of unique items
  5. Algorithm implementation: Graph algorithms, caching, etc.

#Performance Characteristics

  • Insertion: O(1) average case, O(n) worst case
  • Removal: O(1) average case, O(n) worst case
  • Lookup: O(1) average case, O(n) worst case
  • Space complexity: O(n) where n is the number of elements
  • Iteration order: Maintains insertion order (linked hash set)

#Best Practices

  1. Pre-size when possible: Use @set.Set([], capacity=n) if you know the approximate size
  2. Use appropriate types: Ensure your key type has good Hash and Eq implementations
  3. Prefer set operations: Use built-in union, intersection, etc. instead of manual loops
  4. Check return values: Use add_and_check and remove_and_check when you need to know if the operation succeeded
  5. Consider memory usage: Sets have overhead compared to arrays for small collections

#
Set

type Set[K]

Mutable linked hash set that maintains the order of insertion, not thread safe.

Example

test {
let set = @set.Set(["three", "eight", "one"])
@test.assert_eq(set.contains("two"), false)
@test.assert_eq(set.contains("three"), true)
set.add("three") // no effect since it already exists
set.add("two")
@test.assert_eq(set.contains("two"), true)
}
impl BitAnd for Set[K]
impl BitOr for Set[K]
impl BitXOr for Set[K]
impl Default for Set[K]
impl Eq for Set[K]
impl Show for Set[K]
impl Sub for Set[K]
impl ToJson for Set[X]

#
Set::Set

#as_free_fn(of, deprecated="Use from_array instead")
#alias(of, deprecated="Use from_array instead")
#as_free_fn(from_array)
#alias(from_array)
fn[K : Hash + Eq] Set::Set(arr : ArrayView[K], capacity? : Int) -> Set[K]

Creates a hash set containing all elements from the given array, preserving insertion order. The optional capacity is treated as a minimum initial capacity and will be rounded up to the smallest power of 2 that can hold the requested capacity.

#
Set::add

#alias(insert, deprecated="`insert` is deprecated, use `add` instead")
fn[K : Hash + Eq] Set::add(self : Set[K], key : K) -> Unit

Insert a key into the hash set.

Parameters:

  • set : The hash set to modify.
  • key : The key to insert. Must implement Hash and Eq traits.

Example:

test {
let set : @set.Set[String] = Set([])
set.add("key")
inspect(set.contains("key"), content="true")
set.add("key") // no effect since it already exists
inspect(set.length(), content="1")
}

#
Set::add_and_check

fn[K : Hash + Eq] Set::add_and_check(self : Set[K], key : K) -> Bool

Insert a key into the hash set and returns whether the key was successfully added.

Parameters:

  • set : The hash set to modify.
  • key : The key to insert. Must implement Hash and Eq traits.

Returns true if the key was successfully added (i.e., it wasn't already present), false if the key already existed in the set.

Example:

test {
let set : @set.Set[String] = Set([])
inspect(set.add_and_check("key"), content="true") // First insertion
inspect(set.add_and_check("key"), content="false") // Already exists
inspect(set.length(), content="1")
}

#
Set::capacity

fn[K] Set::capacity(self : Set[K]) -> Int

Get the capacity of the set.

#
Set::clear

fn[K] Set::clear(self : Set[K]) -> Unit

Clears the set, removing all keys. Keeps the allocated space.

#
Set::contains

fn[K : Hash + Eq] Set::contains(self : Set[K], key : K) -> Bool

Check if the hash set contains a key.

#
Set::copy

#alias(clone, deprecated="`clone` is deprecated, use `copy` instead")
fn[K] Set::copy(self : Set[K]) -> Set[K]

Copy the set, creating a new set with the same keys and order of insertion.

#
Set::difference

fn[K : Hash + Eq] Set::difference(self : Set[K], other : Set[K]) -> Set[K]

Returns a new set containing elements in self that are not in other.

#
Set::each

fn[K] Set::each(self : Set[K], f : (K) -> Unit raise?) -> Unit raise?

Iterate over all keys of the set in the order of insertion.

#
Set::eachi

fn[K] Set::eachi(self : Set[K], f : (Int, K) -> Unit raise?) -> Unit raise?

Iterate over all keys of the set in the order of insertion, with index.

#
Set::equal

fn[K : Hash + Eq] Set::equal(self : Set[K], other : Set[K]) -> Bool

#
Set::from_iter

#as_free_fn(from_iterator, deprecated="Use Set::from_iter instead.")
#alias(from_iterator, deprecated="`from_iterator` is deprecated, use `from_iter` instead")
#as_free_fn
fn[K : Hash + Eq] Set::from_iter(iter : Iter[K]) -> Set[K]

Create from iter.

#
Set::intersection

fn[K : Hash + Eq] Set::intersection(self : Set[K], other : Set[K]) -> Set[K]

Returns a new set containing only elements present in both sets.

#
Set::is_disjoint

fn[K : Hash + Eq] Set::is_disjoint(self : Set[K], other : Set[K]) -> Bool

Returns true if the two sets have no elements in common.

#
Set::is_empty

fn[K] Set::is_empty(self : Set[K]) -> Bool

Check if the hash set is empty.

#
Set::is_subset

fn[K : Hash + Eq] Set::is_subset(self : Set[K], other : Set[K]) -> Bool

Returns true if every element of self is also in other.

#
Set::is_superset

fn[K : Hash + Eq] Set::is_superset(self : Set[K], other : Set[K]) -> Bool

Returns true if every element of other is also in self.

#
Set::iter

#alias(iterator, deprecated="`iterator` is deprecated, use `iter` instead")
fn[K] Set::iter(self : Set[K]) -> Iter[K]

Returns the iterator of the hash set, provide elements in the order of insertion.

#
Set::land

fn[K : Hash + Eq] Set::land(self : Set[K], other : Set[K]) -> Set[K]

#
Set::length

#alias(size, deprecated="`size` is deprecated, use `length` instead")
fn[K] Set::length(self : Set[K]) -> Int

Get the number of keys in the set.

#
Set::lor

fn[K : Hash + Eq] Set::lor(self : Set[K], other : Set[K]) -> Set[K]

#
Set::lxor

fn[K : Hash + Eq] Set::lxor(self : Set[K], other : Set[K]) -> Set[K]

#
Set::new

#as_free_fn(deprecated="Use `Set([], capacity=...)` instead")
#deprecated("Use `Set([], capacity=...)` instead")
fn[K] Set::new(capacity? : Int) -> Set[K]

Creates an empty insertion-ordered hash set with an optional initial capacity.

Deprecated: use Set([], capacity=...) instead.

#
Set::remove

fn[K : Hash + Eq] Set::remove(self : Set[K], key : K) -> Unit

Remove a key from the hash set. If the key exists in the set, removes it and adjusts the probe sequence length (PSL) of subsequent entries to maintain the Robin Hood hashing invariant. If the key does not exist, the set remains unchanged.

Parameters:

  • self : The hash set to remove the key from.
  • key : The key to remove from the set.

Example:

test {
let set = @set.Set(["a", "b"])
set.remove("a")
inspect(set.contains("a"), content="false")
inspect(set.length(), content="1")
}

#
Set::remove_and_check

fn[K : Hash + Eq] Set::remove_and_check(self : Set[K], key : K) -> Bool

Remove a key from the hash set and returns whether the key was successfully removed.

Parameters:

  • set : The hash set to modify.
  • key : The key to remove. Must implement Hash and Eq traits.

Returns true if the key was successfully removed (i.e., it was present), false if the key didn't exist in the set.

Example:

test {
let set = @set.Set(["a", "b"])
inspect(set.remove_and_check("a"), content="true") // Successfully removed
inspect(set.remove_and_check("a"), content="false") // Already removed
inspect(set.length(), content="1")
}

#
Set::sub

fn[K : Hash + Eq] Set::sub(self : Set[K], other : Set[K]) -> Set[K]

#
Set::symmetric_difference

fn[K : Hash + Eq] Set::symmetric_difference(self : Set[K], other : Set[K]) -> Set[K]

Returns a new set containing elements in exactly one of the two sets.

#
Set::to_array

fn[K] Set::to_array(self : Set[K]) -> Array[K]

Converts the hash set to an array.

#
Set::union

fn[K : Hash + Eq] Set::union(self : Set[K], other : Set[K]) -> Set[K]

Returns a new set containing all elements from both sets.