Skip to content

Commit

Permalink
Update Collection interfaces and bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
moorara committed Dec 25, 2024
1 parent 7f3b890 commit 4864280
Show file tree
Hide file tree
Showing 17 changed files with 665 additions and 233 deletions.
8 changes: 7 additions & 1 deletion generic/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ type (
// If an item does not exist, it is ignored.
Remove(...T)

// RemoveAll removes all items from the collection, leaving it empty.
RemoveAll()

// Contains returns true if the collection includes all the specified items.
Contains(...T) bool

Expand Down Expand Up @@ -66,10 +69,13 @@ type (
// If the key does not exist, the second return value will be false.
Get(K) (V, bool)

// Delete removes the key-value specified by the given key from the collection.
// Delete deletes the key-value specified by the given key from the collection.
// If the key does not exist, the second return value will be false.
Delete(K) (V, bool)

// DeleteAll deletes all key-values from the collection, leaving it empty.
DeleteAll()

// All returns an iterator sequence containing all the key-values in the collection.
// This allows for iterating over the entire collection using the range keyword.
All() iter.Seq2[K, V]
Expand Down
28 changes: 18 additions & 10 deletions set/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,16 @@ type set[T any] struct {
equal EqualFunc[T]
}

// New creates a new empty set.
func New[T any](equal EqualFunc[T]) Set[T] {
return &set[T]{
// New creates a new set.
func New[T any](equal EqualFunc[T], vals ...T) Set[T] {
s := &set[T]{
members: make([]T, 0),
equal: equal,
}

s.Add(vals...)

return s
}

func (s *set[T]) find(v T) int {
Expand Down Expand Up @@ -88,22 +92,26 @@ func (s *set[T]) IsEmpty() bool {
return len(s.members) == 0
}

func (s *set[T]) Add(ss ...T) {
for _, t := range ss {
if !s.Contains(t) {
s.members = append(s.members, t)
func (s *set[T]) Add(vals ...T) {
for _, v := range vals {
if !s.Contains(v) {
s.members = append(s.members, v)
}
}
}

func (s *set[T]) Remove(ss ...T) {
for _, t := range ss {
if i := s.find(t); i != -1 {
func (s *set[T]) Remove(vals ...T) {
for _, v := range vals {
if i := s.find(v); i != -1 {
s.members = append(s.members[:i], s.members[i+1:]...)
}
}
}

func (s *set[T]) RemoveAll() {
s.members = make([]T, 0)
}

func (s *set[T]) Contains(vals ...T) bool {
for _, v := range vals {
if s.find(v) == -1 {
Expand Down
72 changes: 62 additions & 10 deletions set/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,28 @@ import (
)

func TestNew(t *testing.T) {
set := New(NewEqualFunc[string]())
assert.NotNil(t, set)
tests := []struct {
name string
equal EqualFunc[string]
vals []string
expectedMembers []string
}{
{
name: "OK",
equal: NewEqualFunc[string](),
vals: []string{"a", "b", "c", "d"},
expectedMembers: []string{"a", "b", "c", "d"},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
s := New(tc.equal, tc.vals...)

assert.NotNil(t, s)
assert.Equal(t, tc.expectedMembers, s.(*set[string]).members)
})
}
}

func TestSet_String(t *testing.T) {
Expand Down Expand Up @@ -191,7 +211,7 @@ func TestSet_Add(t *testing.T) {
tests := []struct {
name string
s *set[string]
ss []string
vals []string
expectedMembers []string
}{
{
Expand All @@ -200,7 +220,7 @@ func TestSet_Add(t *testing.T) {
members: []string{},
equal: eqFunc,
},
ss: []string{"a", "b", "c", "d"},
vals: []string{"a", "b", "c", "d"},
expectedMembers: []string{"a", "b", "c", "d"},
},
{
Expand All @@ -209,14 +229,14 @@ func TestSet_Add(t *testing.T) {
members: []string{"a", "b"},
equal: eqFunc,
},
ss: []string{"a", "c", "d"},
vals: []string{"a", "c", "d"},
expectedMembers: []string{"a", "b", "c", "d"},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
tc.s.Add(tc.ss...)
tc.s.Add(tc.vals...)
assert.Equal(t, tc.expectedMembers, tc.s.members)
})
}
Expand All @@ -228,7 +248,7 @@ func TestSet_Remove(t *testing.T) {
tests := []struct {
name string
s *set[string]
ss []string
vals []string
expectedMembers []string
}{
{
Expand All @@ -237,7 +257,7 @@ func TestSet_Remove(t *testing.T) {
members: []string{},
equal: eqFunc,
},
ss: []string{"a", "b"},
vals: []string{"a", "b"},
expectedMembers: []string{},
},
{
Expand All @@ -246,19 +266,51 @@ func TestSet_Remove(t *testing.T) {
members: []string{"a", "b", "c", "d"},
equal: eqFunc,
},
ss: []string{"a", "c"},
vals: []string{"a", "c"},
expectedMembers: []string{"b", "d"},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
tc.s.Remove(tc.ss...)
tc.s.Remove(tc.vals...)
assert.Equal(t, tc.expectedMembers, tc.s.members)
})
}
}

func TestSet_RemoveAll(t *testing.T) {
eqFunc := NewEqualFunc[string]()

tests := []struct {
name string
s *set[string]
}{
{
name: "Empty",
s: &set[string]{
members: []string{},
equal: eqFunc,
},
},
{
name: "NonEmpty",
s: &set[string]{
members: []string{"a", "b", "c", "d"},
equal: eqFunc,
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
tc.s.RemoveAll()
assert.Zero(t, tc.s.Size())
assert.True(t, tc.s.IsEmpty())
})
}
}

func TestSet_Contains(t *testing.T) {
eqFunc := NewEqualFunc[string]()

Expand Down
11 changes: 8 additions & 3 deletions symboltable/avl.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func (t *avl[K, V]) _get(n *avlNode[K, V], key K) (V, bool) {
}
}

// Delete removes a key-value from the AVL tree.
// Delete deletes a key-value from the AVL tree.
func (t *avl[K, V]) Delete(key K) (val V, ok bool) {
t.root, val, ok = t._delete(t.root, key)
return val, ok
Expand Down Expand Up @@ -276,6 +276,11 @@ func (t *avl[K, V]) _delete(n *avlNode[K, V], key K) (*avlNode[K, V], V, bool) {
return t.balance(n), val, ok
}

// DeleteAll deletes all key-values from the AVL tree, leaving it empty.
func (t *avl[K, V]) DeleteAll() {
t.root = nil
}

// Min returns the minimum key and its value in the AVL tree.
func (t *avl[K, V]) Min() (K, V, bool) {
if t.root == nil {
Expand Down Expand Up @@ -376,7 +381,7 @@ func (t *avl[K, V]) _ceiling(n *avlNode[K, V], key K) *avlNode[K, V] {
return n
}

// DeleteMin removes the smallest key and associated value from the AVL tree.
// DeleteMin deletes the smallest key and associated value from the AVL tree.
func (t *avl[K, V]) DeleteMin() (K, V, bool) {
if t.root == nil {
var zeroK K
Expand All @@ -401,7 +406,7 @@ func (t *avl[K, V]) _deleteMin(n *avlNode[K, V]) (*avlNode[K, V], *avlNode[K, V]
return t.balance(n), min
}

// DeleteMax removes the largest key and associated value from the AVL tree.
// DeleteMax deletes the largest key and associated value from the AVL tree.
func (t *avl[K, V]) DeleteMax() (K, V, bool) {
if t.root == nil {
var zeroK K
Expand Down
11 changes: 8 additions & 3 deletions symboltable/bst.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (t *bst[K, V]) _get(n *bstNode[K, V], key K) (V, bool) {
}
}

// Delete removes a key-value from the BST.
// Delete deletes a key-value from the BST.
func (t *bst[K, V]) Delete(key K) (val V, ok bool) {
t.root, val, ok = t._delete(t.root, key)
return val, ok
Expand Down Expand Up @@ -214,6 +214,11 @@ func (t *bst[K, V]) _delete(n *bstNode[K, V], key K) (*bstNode[K, V], V, bool) {
return n, val, ok
}

// DeleteAll deletes all key-values from the BST, leaving it empty.
func (t *bst[K, V]) DeleteAll() {
t.root = nil
}

// Min returns the minimum key and its value in the BST.
func (t *bst[K, V]) Min() (K, V, bool) {
if t.root == nil {
Expand Down Expand Up @@ -314,7 +319,7 @@ func (t *bst[K, V]) _ceiling(n *bstNode[K, V], key K) *bstNode[K, V] {
return n
}

// DeleteMin removes the smallest key and associated value from the BST.
// DeleteMin deletes the smallest key and associated value from the BST.
func (t *bst[K, V]) DeleteMin() (K, V, bool) {
if t.root == nil {
var zeroK K
Expand All @@ -338,7 +343,7 @@ func (t *bst[K, V]) _deleteMin(n *bstNode[K, V]) (*bstNode[K, V], *bstNode[K, V]
return n, min
}

// DeleteMax removes the largest key and associated value from the BST.
// DeleteMax deletes the largest key and associated value from the BST.
func (t *bst[K, V]) DeleteMax() (K, V, bool) {
if t.root == nil {
var zeroK K
Expand Down
21 changes: 17 additions & 4 deletions symboltable/chain_hash_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

const (
scMinM = 4 // Minimum number of buckets in the hash table (must be at least 4 and a power of 2 for efficient hashing)
scMinM = 4 // Minimum number of buckets in the hash table (must be a power of 2 for efficient hashing)
scMinLoadFactor = 2.0 // Minimum load factor before resizing (shrinking)
scMaxLoadFactor = 10.0 // Maximum load factor before resizing (expanding)
)
Expand Down Expand Up @@ -53,7 +53,9 @@ func NewChainHashTable[K, V any](hashKey HashFunc[K], eqKey EqualFunc[K], eqVal
opts.MaxLoadFactor = scMaxLoadFactor
}

opts.verify()
if M := opts.InitialCap; M < scMinM || !isPowerOf2(M) {
panic(fmt.Sprintf("The hash table capacity must be at least %d and a power of 2 for efficient hashing.", scMinM))
}

return &chainHashTable[K, V]{
buckets: make([]*chainNode[K, V], opts.InitialCap),
Expand Down Expand Up @@ -105,6 +107,11 @@ func (ht *chainHashTable[K, V]) hash(key K) int {

// resize adjusts the hash table to a new size and re-hashes all keys.
func (ht *chainHashTable[K, V]) resize(m int) {
// Ensure the minimum table size
if m < scMinM {
return
}

newHT := NewChainHashTable[K, V](ht.hashKey, ht.eqKey, ht.eqVal, HashOpts{
InitialCap: m,
MinLoadFactor: ht.minLF,
Expand Down Expand Up @@ -167,12 +174,12 @@ func (ht *chainHashTable[K, V]) Get(key K) (V, bool) {
return zeroV, false
}

// Delete removes a key-value from the hash table.
// Delete deletes a key-value from the hash table.
func (ht *chainHashTable[K, V]) Delete(key K) (val V, ok bool) {
i := ht.hash(key)
ht.buckets[i], val, ok = ht._delete(ht.buckets[i], key)

if ht.m > scMinM && ht.loadFactor() <= ht.minLF {
if ht.loadFactor() <= ht.minLF {
ht.resize(ht.m / 2)
}

Expand All @@ -197,6 +204,12 @@ func (ht *chainHashTable[K, V]) _delete(n *chainNode[K, V], key K) (*chainNode[K
return n, val, ok
}

// DeleteAll deletes all key-values from the hash table, leaving it empty.
func (ht *chainHashTable[K, V]) DeleteAll() {
ht.buckets = make([]*chainNode[K, V], ht.m)
ht.n = 0
}

// String returns a string representation of the hash table.
func (ht *chainHashTable[K, V]) String() string {
pairs := make([]string, ht.Size())
Expand Down
Loading

0 comments on commit 4864280

Please sign in to comment.