Implement hash set

This commit is contained in:
Denis-Cosmin Nutiu 2024-09-15 15:00:16 +03:00
parent 031c9f6be8
commit acae9a6b41
5 changed files with 234 additions and 25 deletions

View file

@ -0,0 +1,92 @@
package hash_set
import (
"fmt"
"strings"
)
type MyHash interface {
~string | ~int | ~uint | ~int64 | ~uint64 | ~int32 | ~uint32 | ~int16 | ~uint16 | ~int8 | ~uint8
}
type Hasher[H MyHash] interface {
Hash() H
}
// MyHashSet is a hash set implementation.
type MyHashSet[T Hasher[H], H MyHash] struct {
storage map[H]T
}
// NewSet initializes a new hash set.
func NewSet[T Hasher[H], H MyHash]() MyHashSet[T, H] {
return MyHashSet[T, H]{
storage: make(map[H]T, 100),
}
}
// Add adds an element to a set.
func (s *MyHashSet[T, H]) Add(element T) {
// Hash element
hash := element.Hash()
// Save
_, ok := s.storage[hash]
if !ok {
s.storage[hash] = element
}
}
// AddAll adds all the elements to the set.
func (s *MyHashSet[T, H]) AddAll(elements ...T) {
for _, element := range elements {
s.Add(element)
}
}
// Contains checks if the hash set contains the element T.
// Returns true if the element is part of the set, false otherwise.
func (s *MyHashSet[T, H]) Contains(element T) bool {
// Hash element
hash := element.Hash()
_, ok := s.storage[hash]
return ok
}
// Delete deletes an element from the set.
// Returns true if the element was deleted, false otherwise.
func (s *MyHashSet[T, H]) Delete(element T) bool {
// Hash element
hash := element.Hash()
_, ok := s.storage[hash]
if ok {
delete(s.storage, hash)
return true
}
return false
}
// String returns the string representation of the set.
func (s *MyHashSet[T, H]) String() string {
var sb strings.Builder
sb.WriteString("MyHashSet{")
for _, value := range s.storage {
sb.WriteString(fmt.Sprintf("%v,", value))
}
sb.WriteString("}")
return sb.String()
}
// Union creates a union of two sets
func (s *MyHashSet[T, H]) Union(other *MyHashSet[T, H]) {
for _, v := range other.storage {
s.Add(v)
}
}
// Sub creates a difference of two sets
func (s *MyHashSet[T, H]) Sub(other *MyHashSet[T, H]) {
for _, v := range other.storage {
s.Delete(v)
}
}

View file

@ -0,0 +1,85 @@
package hash_set
import (
"github.com/stretchr/testify/assert"
"testing"
)
type MyString struct {
Value string
}
func (m MyString) Hash() string {
return m.Value
}
func TestNewSet(t *testing.T) {
newSet := NewSet[MyString, string]()
assert.NotNil(t, newSet)
}
func TestMyHashSet_Add(t *testing.T) {
// Given
newSet := NewSet[MyString, string]()
newSet.Add(MyString{Value: "some"})
// Then
assert.True(t, newSet.Contains(MyString{Value: "some"}))
}
func TestMyHashSet_AddAll(t *testing.T) {
// Given
newSet := NewSet[MyString, string]()
newSet.AddAll(MyString{Value: "some"}, MyString{Value: "another"})
// Then
assert.True(t, newSet.Contains(MyString{Value: "some"}))
assert.True(t, newSet.Contains(MyString{Value: "another"}))
}
func TestMyHashSet_Delete(t *testing.T) {
// Given
newSet := NewSet[MyString, string]()
newSet.AddAll(MyString{Value: "some"}, MyString{Value: "another"})
newSet.Delete(MyString{Value: "some"})
// Then
assert.False(t, newSet.Contains(MyString{Value: "some"}))
assert.True(t, newSet.Contains(MyString{Value: "another"}))
}
func TestMyHashSet_Union(t *testing.T) {
// Setup
newSet := NewSet[MyString, string]()
newSet.AddAll(MyString{Value: "some"}, MyString{Value: "another"})
anotherSet := NewSet[MyString, string]()
anotherSet.AddAll(MyString{Value: "Batman"}, MyString{Value: "Robin"})
// Test
newSet.Union(&anotherSet)
// Then
assert.True(t, newSet.Contains(MyString{Value: "some"}))
assert.True(t, newSet.Contains(MyString{Value: "another"}))
assert.True(t, newSet.Contains(MyString{Value: "Batman"}))
assert.True(t, newSet.Contains(MyString{Value: "Robin"}))
}
func TestMyHashSet_Sub(t *testing.T) {
// Setup
newSet := NewSet[MyString, string]()
newSet.AddAll(MyString{Value: "some"}, MyString{Value: "another"}, MyString{Value: "Batman"})
anotherSet := NewSet[MyString, string]()
anotherSet.AddAll(MyString{Value: "Batman"}, MyString{Value: "Robin"})
// Test
newSet.Sub(&anotherSet)
// Then
assert.True(t, newSet.Contains(MyString{Value: "some"}))
assert.True(t, newSet.Contains(MyString{Value: "another"}))
assert.False(t, newSet.Contains(MyString{Value: "Batman"}))
assert.False(t, newSet.Contains(MyString{Value: "Robin"}))
}

51
hash_set/main.go Normal file
View file

@ -0,0 +1,51 @@
package main
import (
"fmt"
"go-dsa/hash_set/v2/hash_set"
)
type Person struct {
Name string
Age int
}
// Hash returns the has of a person, it conforms to the hash_set.Hasher interface
func (p Person) Hash() string {
return fmt.Sprintf("%s-%d", p.Name, p.Age)
}
func main() {
mySet := hash_set.NewSet[Person, string]()
// Add
mySet.Add(Person{
Name: "Batman",
Age: 28,
})
mySet.Add(Person{
Name: "Robin",
Age: 16,
})
mySet.Add(Person{
Name: "Batman",
Age: 28,
})
// Print
fmt.Printf("%s\n", mySet.String())
// Contains
result := mySet.Contains(Person{
Name: "Batman",
Age: 28,
})
fmt.Printf("Set contains batman %v\n", result)
// Deletion
mySet.Delete(Person{
Name: "Batman",
Age: 28,
})
fmt.Printf("%s\n", mySet.String())
}

25
main.go
View file

@ -1,25 +0,0 @@
package main
import (
"fmt"
)
//TIP To run your code, right-click the code and select <b>Run</b>. Alternatively, click
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.
func main() {
//TIP Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined or highlighted text
// to see how GoLand suggests fixing it.
s := "gopher"
fmt.Println("Hello and welcome, %s!", s)
for i := 1; i <= 5; i++ {
//TIP You can try debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>. To start your debugging session,
// right-click your code in the editor and select the <b>Debug</b> option.
fmt.Println("i =", 100/i)
}
}
//TIP See GoLand help at <a href="https://www.jetbrains.com/help/go/">jetbrains.com/help/go/</a>.
// Also, you can try interactive lessons for GoLand by selecting 'Help | Learn IDE Features' from the main menu.

6
readme.md Normal file
View file

@ -0,0 +1,6 @@
# Data Structures and Algorithms
This repository is my self study guide for data structures and algorithms. I implement them from scratch in Go
and then write unit test for them.
What better way to learn a language and new concepts exists other than practicing them.