Skip to content

Commit

Permalink
common: some tweaks for common helper functions
Browse files Browse the repository at this point in the history
This PR renames TransformUnion to InsertSetFunc, and TransformSlice to
SliceFunc which should be a bit more obvious to understand. Also, the
"Union" methods on the set types are ones that return a newly created set,
and so we are also now more consistent with how those are named.

Also adds a boolean return value to InsertSetFunc, which like the insertion
methods on the set types returns true if the set was modified as a result
of the operation. And adds test cases around being modified or not modified.

Cleans up a few doc strings.
  • Loading branch information
shoenig committed May 16, 2023
1 parent 8c0267d commit c9971c0
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 32 deletions.
47 changes: 33 additions & 14 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,32 @@

package set

// Common is the interface that all sets implement
// Common is a minimal interface that all sets implement.
type Common[T any] interface {
// Slice returns a slice of all elements in the set

// Slice returns a slice of all elements in the set.
//
// Note: order of elements depends on the underlying implementation.
Slice() []T
// Insert inserts an element into the set
// if the element already exists, it will return false

// Insert an element into the set.
//
// Returns true if the set is modified as a result.
Insert(T) bool
// InsertSlice inserts all elements from the slice into the set

// InsertSlice inserts all elements from the slice into the set.
//
// Returns true if the set was modified as a result.
InsertSlice([]T) bool
// Size returns the number of elements in the set

// Size returns the number of elements in the set.
Size() int
// ForEach will call the callback function for each element in the set.

// ForEach will call the callback function for each element in the set.
// If the callback returns false, the iteration will stop.
// Note: iteration order depends on the underlying implementation;
ForEach(call func(T) bool)
//
// Note: iteration order depends on the underlying implementation.
ForEach(func(T) bool)
}

// InsertSliceFunc inserts all elements from the slice into the set
Expand All @@ -27,16 +38,24 @@ func InsertSliceFunc[T, E any](s Common[T], items []E, f func(element E) T) {
}
}

// TransformUnion transforms the set A into another set B
func TransformUnion[T, E any](a Common[T], b Common[E], transform func(T) E) {
// InsertSetFunc inserts the elements of a into b, applying the transform function
// to each element before insertion.
//
// Returns true if b was modified as a result.
func InsertSetFunc[T, E any](a Common[T], b Common[E], transform func(T) E) bool {
modified := false
a.ForEach(func(item T) bool {
_ = b.Insert(transform(item))
if b.Insert(transform(item)) {
modified = true
}
return true
})
return modified
}

// TransformSlice transforms the set into a slice
func TransformSlice[T, E any](s Common[T], transform func(T) E) []E {
// SliceFunc produces a slice of the elements in s, applying the transform
// function to each element first.
func SliceFunc[T, E any](s Common[T], transform func(T) E) []E {
slice := make([]E, 0, s.Size())
s.ForEach(func(item T) bool {
slice = append(slice, transform(item))
Expand Down
81 changes: 63 additions & 18 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ import (

func TestInsertSliceFunc(t *testing.T) {
numbers := ints(3)

t.Run("set", func(t *testing.T) {
s := New[string](10)
transform := func(element int) string { return strconv.Itoa(element) }
InsertSliceFunc[string, int](s, numbers, transform)
InsertSliceFunc[string](s, numbers, transform)
slices := s.Slice()
sort.Strings(slices)
must.SliceEqFunc(t, slices, []string{"1", "2", "3"}, func(a, b string) bool { return a == b })
})

t.Run("hashset", func(t *testing.T) {
s := NewHashSet[*company, string](10)
InsertSliceFunc[*company, int](s, numbers, func(element int) *company {
InsertSliceFunc[*company](s, numbers, func(element int) *company {
return &company{
address: "InsertSliceFunc",
floor: element,
Expand All @@ -33,21 +35,21 @@ func TestInsertSliceFunc(t *testing.T) {
"InsertSliceFunc:1", "InsertSliceFunc:2", "InsertSliceFunc:3",
})
})

t.Run("treeSet", func(t *testing.T) {
s := NewTreeSet[string, Compare[string]](Cmp[string])
InsertSliceFunc[string, int](s, numbers, func(element int) string {
InsertSliceFunc[string](s, numbers, func(element int) string {
return strconv.Itoa(element)
})
invariants(t, s, Cmp[string])
must.SliceEqFunc(t, s.Slice(), []string{"1", "2", "3"}, func(a, b string) bool { return a == b })
})
}

func TestTransformSlice(t *testing.T) {
func TestSliceFunc(t *testing.T) {
t.Run("set", func(t *testing.T) {
s := From(ints(3))

slice := TransformSlice[int, string](s, func(element int) string {
slice := SliceFunc[int](s, func(element int) string {
return strconv.Itoa(element)
})
sort.Strings(slice)
Expand All @@ -57,122 +59,165 @@ func TestTransformSlice(t *testing.T) {
t.Run("hashset", func(t *testing.T) {
s := NewHashSet[*company, string](10)
s.InsertSlice([]*company{c1, c2, c3})
slice := TransformSlice[*company, string](s, func(element *company) string {
slice := SliceFunc[*company](s, func(element *company) string {
return element.Hash()
})
sort.Strings(slice)
must.SliceEqFunc(t, slice, []string{"street:1", "street:2", "street:3"}, func(a, b string) bool { return a == b })
})

t.Run("treeSet", func(t *testing.T) {
s := TreeSetFrom[int, Compare[int]]([]int{1, 2, 3}, Cmp[int])
slice := TransformSlice[int, string](s, func(element int) string {
slice := SliceFunc[int](s, func(element int) string {
return strconv.Itoa(element)
})
sort.Strings(slice)
must.SliceEqFunc(t, slice, []string{"1", "2", "3"}, func(a, b string) bool { return a == b })
})
}

func TestTransform(t *testing.T) {
func TestInsertSetFunc(t *testing.T) {
t.Run("set", func(t *testing.T) {
a := From(ints(3))
t.Run("set -> set", func(t *testing.T) {
b := New[string](3)
TransformUnion[int, string](a, b, func(element int) string {
modified := InsertSetFunc[int, string](a, b, func(element int) string {
return strconv.Itoa(element)
})
must.True(t, modified)
slice := b.Slice()
sort.Strings(slice)
must.SliceEqFunc(t, slice, []string{"1", "2", "3"}, func(a, b string) bool { return a == b })
})

t.Run("set -> hashset", func(t *testing.T) {
b := NewHashSet[*company, string](10)
TransformUnion[int, *company](a, b, func(element int) *company {
modified := InsertSetFunc[int, *company](a, b, func(element int) *company {
return &company{
address: "street",
floor: element,
}
})
must.True(t, modified)
must.MapContainsKeys(t, b.items, []string{
"street:1", "street:2", "street:3",
})
})

t.Run("set -> treeSet", func(t *testing.T) {
b := NewTreeSet[string, Compare[string]](Cmp[string])
TransformUnion[int, string](a, b, func(element int) string {
modified := InsertSetFunc[int, string](a, b, func(element int) string {
return strconv.Itoa(element)
})
must.True(t, modified)
slice := b.Slice()
sort.Strings(slice)
must.SliceEqFunc(t, slice, []string{"1", "2", "3"}, func(a, b string) bool { return a == b })
})

t.Run("not modified", func(t *testing.T) {
b := a.Copy()
modified := InsertSetFunc[int, int](a, b, func(element int) int {
return element
})
must.False(t, modified)
})
})

t.Run("hashSet", func(t *testing.T) {
a := NewHashSet[*company, string](10)
a.InsertSlice([]*company{c1, c2, c3})

t.Run("hashSet -> set", func(t *testing.T) {
b := New[int](3)
TransformUnion[*company, int](a, b, func(element *company) int {
modified := InsertSetFunc[*company, int](a, b, func(element *company) int {
return element.floor
})
must.True(t, modified)
slice := b.Slice()
sort.Ints(slice)
must.SliceEqFunc(t, slice, []int{1, 2, 3}, func(a, b int) bool { return a == b })
})

t.Run("hashSet -> hashSet", func(t *testing.T) {
b := NewHashSet[*company, string](10)
TransformUnion[*company, *company](a, b, func(element *company) *company {
modified := InsertSetFunc[*company, *company](a, b, func(element *company) *company {
return &company{
address: element.address,
floor: element.floor * 5,
}
})
must.True(t, modified)
must.MapContainsKeys(t, b.items, []string{
"street:5", "street:10", "street:15",
})
})

t.Run("hashSet -> treeSet", func(t *testing.T) {
b := NewTreeSet[int, Compare[int]](Cmp[int])
TransformUnion[*company, int](a, b, func(element *company) int {
modified := InsertSetFunc[*company, int](a, b, func(element *company) int {
return element.floor
})
must.True(t, modified)
slice := b.Slice()
sort.Ints(slice)
must.SliceEqFunc(t, slice, []int{1, 2, 3}, func(a, b int) bool { return a == b })
})

t.Run("not modified", func(t *testing.T) {
b := a.Copy()
modified := InsertSetFunc[*company, *company](a, b, func(element *company) *company {
return element
})
must.False(t, modified)
})
})

t.Run("treeSet", func(t *testing.T) {
a := TreeSetFrom[int, Compare[int]]([]int{1, 2, 3}, Cmp[int])

t.Run("treeSet -> set", func(t *testing.T) {
b := New[string](3)
TransformUnion[int, string](a, b, func(element int) string {
modified := InsertSetFunc[int, string](a, b, func(element int) string {
return strconv.Itoa(element)
})
must.True(t, modified)
slice := b.Slice()
sort.Strings(slice)
must.SliceEqFunc(t, slice, []string{"1", "2", "3"}, func(a, b string) bool { return a == b })
})

t.Run("treeSet -> hashSet", func(t *testing.T) {
b := NewHashSet[*company, string](10)
TransformUnion[int, *company](a, b, func(element int) *company {
modified := InsertSetFunc[int, *company](a, b, func(element int) *company {
return &company{
address: "street",
floor: element,
}
})
must.True(t, modified)
must.MapContainsKeys(t, b.items, []string{
"street:1", "street:2", "street:3",
})
})

t.Run("treeSet -> treeSet", func(t *testing.T) {
b := NewTreeSet[string, Compare[string]](Cmp[string])
TransformUnion[int, string](a, b, func(element int) string {
modified := InsertSetFunc[int, string](a, b, func(element int) string {
return strconv.Itoa(element)
})
must.True(t, modified)
slice := b.Slice()
sort.Strings(slice)
must.SliceEqFunc(t, slice, []string{"1", "2", "3"}, func(a, b string) bool { return a == b })
})

t.Run("not modified", func(t *testing.T) {
b := a.Copy()
modified := InsertSetFunc[int, int](a, b, func(element int) int {
return element
})
must.False(t, modified)
})
})
}

0 comments on commit c9971c0

Please sign in to comment.