Skip to content

Commit

Permalink
feat: Add basic operation with testing
Browse files Browse the repository at this point in the history
- Add converting to slice when initializing
- Test Add
- Test Check membership
- Test Remove
- Test Contains
- Test Duplicate values
- Test Convert to set
- Test Convert to slice
- Test Union operation(Set operation)
  • Loading branch information
1eedaegon committed Mar 30, 2024
1 parent 7ea9161 commit 60fc837
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 30 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ import (
s.Remove("3") // The length of s is 2, because due to the difference in types.
```

Remove and Distinguish between different types

```go
import (
...
hashset "github.com/1eedaegon/go-hashset"
...
)
s := hashset.New("1", "2", 3)
s.Remove("1")
s.Remove("multiple")
s.Remove("3") // The length of s is 2, because due to the difference in types.
```

## License

[MIT](LICENSE)
31 changes: 20 additions & 11 deletions hashset.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,19 @@ type Set struct {
}

// New initializes and returns a new Set with optional initial elements.
func New(initial ...interface{}) *Set {
func New(initialValue ...interface{}) *Set {
s := &Set{
hash: make(map[interface{}]bool),
}
for _, v := range initial {
s.Add(v)
for _, iv := range initialValue {
v := reflect.ValueOf(iv)
if v.Kind() == reflect.Slice {
for i := 0; i < v.Len(); i++ {
s.Add(v.Index(i).Interface())
}
} else {
s.Add(iv)
}
}
return s
}
Expand All @@ -26,25 +33,29 @@ func New(initial ...interface{}) *Set {
func (s *Set) Add(element interface{}) {
s.mu.Lock()
defer s.mu.Unlock()
if IsComparable(element) {
s.hash[element] = true
return
if !IsComparable(element) {
element = MakeComparable(element)
}
element = MakeComparable(element)
s.hash[element] = true
}

// Remove deletes an element from the Set. If the element is not present, it does nothing.
func (s *Set) Remove(element interface{}) {
s.mu.Lock()
defer s.mu.Unlock()
if !IsComparable(element) {
element = MakeComparable(element)
}
delete(s.hash, element)
}

// Contains checks if an element is present in the Set.
func (s *Set) Contains(element interface{}) bool {
s.mu.RLock()
defer s.mu.RUnlock()
if !IsComparable(element) {
element = MakeComparable(element)
}
_, exists := s.hash[element]
return exists
}
Expand Down Expand Up @@ -123,6 +134,7 @@ func (s *Set) Union(set *Set) *Set {
return &Set{hash: union}
}

// ToSlice function returns converted slice from this set
func (s *Set) ToSlice() []interface{} {
uniTypeSlice := make([]interface{}, 0)
s.mu.RLock()
Expand All @@ -133,14 +145,11 @@ func (s *Set) ToSlice() []interface{} {
return uniTypeSlice
}

// MakeComparable returns pointer(address) not comparable types: slice, map, function
func MakeComparable(element interface{}) interface{} {
/*
Not comparable types: slice, map, function
*/
// defer func() {
// if r := recover(); r != nil {
// }
// }()
elementType := reflect.TypeOf(element)
switch elementType.Kind() {
case reflect.Slice, reflect.Map, reflect.Func:
Expand Down
128 changes: 109 additions & 19 deletions hashset_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hashset

import (
"reflect"
"strconv"
"sync"
"testing"
Expand All @@ -10,11 +11,6 @@ import (
)

/*
0. 기본 연산
1. 추가
2. 삭제
3. 요소 확인
1. 값의 중복이 없어야한다.
2. slice가오면 set으로 변경되어야한다.
3. set을 slice로 변경할 수 있어야한다.
4. set(집합) 연산이 가능해야한다.
Expand All @@ -35,8 +31,8 @@ func TestInitializeFromArguments(t *testing.T) {
require.NotEqual(t, 4, s.Len())

s1 := New([]int{1, 2, 3})
require.Equal(t, 1, s1.Len()) // Not comparable type, just save pointer
require.False(t, s1.Contains(2))
require.Equal(t, 3, s1.Len())
require.True(t, s1.Contains(2))
}

// Basic operations: 1. Add element
Expand Down Expand Up @@ -64,17 +60,111 @@ func TestRemoveElement(t *testing.T) {
require.Equal(t, 2, s.Len())
}

// Basic operations: 3.
// func TestMembershipCheck(t *testing.T) {}

// func TestDuplicate(t *testing.T) {}
// func TestConvertToSet(t *testing.T) {}
// func TestConvertToSlice(t *testing.T) {}
// func TestUnion(t *testing.T) {}
// func TestIntersection(t *testing.T) {}
// func TestDifference(t *testing.T) {}
// func TestFunctionElement(t *testing.T) {}
// func TestStructElement(t *testing.T) {}
// Basic operations: 3. Check membership in set
func TestMembershipCheck(t *testing.T) {
caseFunction := func() bool { return true }
s := New("1", "2", "3", 1, caseFunction)
require.NotEmpty(t, s)
require.Equal(t, 5, s.Len())

require.True(t, s.Contains("1"))
require.True(t, s.Contains("2"))
require.True(t, s.Contains("3"))
require.False(t, s.Contains(2))
require.True(t, s.Contains(caseFunction))
}

func TestDuplicate(t *testing.T) {
// Same address testing
caseFunction := func() bool { return true }
caseMap := map[string]int{}
caseSlice := []int{1, 2, 3}
caseSliceTwo := []string{"1", "a", "b"}

s := New(caseFunction, caseMap, caseSlice, caseSliceTwo)
require.Equal(t, 8, s.Len())
s.Add(caseFunction)
s.Add(caseMap)
s.Add(caseSlice)
s.Add(caseSliceTwo)
require.Equal(t, 10, s.Len())

// However, if the same signature arguments address are different, it is not a duplicate
caseFunction2 := func() bool { return true }
caseMap2 := map[string]int{}
caseSlice2 := []int{1, 2, 3}
caseSliceTwo2 := []string{"1", "a", "b"}
s.Add(caseFunction2)
s.Add(caseMap2)
s.Add(caseSlice2)
s.Add(caseSliceTwo2)
// require.NotEqual(t, 4, s.Len())
require.Equal(t, 14, s.Len())

// Comparable types are duplicated checked.
s.Add(1)
s.Add(2)
s.Add("caseSlice2")
s.Add("caseSliceTwo2")
require.Equal(t, 16, s.Len())
s.Add(1)
s.Add(2)
s.Add("caseSlice2")
s.Add("caseSliceTwo2")
require.Equal(t, 16, s.Len())
}

func TestConvertToSet(t *testing.T) {
caseSlice := []int{1, 2, 3}
caseSliceTwo := []string{"1", "a", "b"}
s := New(caseSlice, caseSliceTwo)
require.Equal(t, 6, s.Len())
require.True(t, s.Contains(1))
require.True(t, s.Contains("1"))
require.True(t, s.Contains("b"))
}

func TestConvertToSlice(t *testing.T) {
caseSlice := []int{1, 2, 3}
caseSliceTwo := []string{"1", "a", "b"}
s := New(caseSlice, caseSliceTwo)
require.Equal(t, 6, s.Len())
arr := s.ToSlice()
require.Equal(t, 6, len(arr))
require.True(t, reflect.ValueOf(arr).Kind() == reflect.Slice)
}

func TestUnion(t *testing.T) {
caseSlice := []int{1, 2, 3}
caseSliceTwo := []int{3, 4, 5}
s1 := New(caseSlice)
require.Equal(t, 3, s1.Len())
s2 := New(caseSliceTwo)
require.Equal(t, 3, s2.Len())

// Union numbers
s3 := s1.Union(s2)
require.Equal(t, 5, s3.Len())
require.True(t, s3.Contains(1))
require.True(t, s3.Contains(5))
require.True(t, s3.Contains(3))

// Union other types
caseSliceThree := []interface{}{1, 4, 5, "1", "5"}
s4 := New(caseSliceThree)
s5 := s3.Union(s4)
require.Equal(t, 7, s5.Len())
require.True(t, s5.Contains(1))
require.True(t, s5.Contains(5))
require.True(t, s5.Contains("5"))
}

func TestIntersection(t *testing.T) {

}
func TestDifference(t *testing.T) {}
func TestFunctionElement(t *testing.T) {}
func TestStructElement(t *testing.T) {}
func TestConcurrentAddElement10Goroutine100000Loop(t *testing.T) {
var wg sync.WaitGroup
s := New()
Expand Down Expand Up @@ -180,7 +270,7 @@ func TestConcurrentRemoveElement(t *testing.T) {
require.Equal(t, 0, s.Len())
}

func TestMembershipCheck(t *testing.T) {
func TestConcurrentMembershipCheck(t *testing.T) {
var wg sync.WaitGroup

s := New()
Expand Down

0 comments on commit 60fc837

Please sign in to comment.