set: mostly done first version
parent
4cb1c352a9
commit
99865a3ccd
@ -1,2 +1,3 @@
|
||||
# collections-go
|
||||
Generic collections to use with Go >1.18
|
||||
|
||||
A dependency-free package of generic Go collection data structures. Compatible with Go 1.18 and above.
|
||||
|
@ -0,0 +1,5 @@
|
||||
module github.com/RageCage64/collections-go
|
||||
|
||||
go 1.18
|
||||
|
||||
require github.com/RageCage64/go-assert v0.2.2
|
@ -0,0 +1,4 @@
|
||||
github.com/RageCage64/go-assert v0.2.1 h1:xTDJnm9q2IDdXKKUVUMEr3pCnwQCiubB5gu+t60NbNY=
|
||||
github.com/RageCage64/go-assert v0.2.1/go.mod h1:YuWzhAJlE4Z2TW2D4msNx1mNU0wA+6lmAL0lP1l7yd4=
|
||||
github.com/RageCage64/go-assert v0.2.2 h1:wwPA2yibB7XmaQKpw4xk7NfVPdJ1v79GlBvmS8P9ROM=
|
||||
github.com/RageCage64/go-assert v0.2.2/go.mod h1:YuWzhAJlE4Z2TW2D4msNx1mNU0wA+6lmAL0lP1l7yd4=
|
@ -0,0 +1,123 @@
|
||||
package collections
|
||||
|
||||
// Set is a type alias over a map to an empty struct. This allows a nice
|
||||
// API over the hash map functionality already available, where you can instead
|
||||
// view the set as a single dimension collection and save a lot of clutter of
|
||||
// empty struct initialization any time you add to the set.
|
||||
type Set[T comparable] map[T]struct{}
|
||||
|
||||
// Initialize a new Set by providing a slice. The Set will have the
|
||||
// same type as the slice.
|
||||
//
|
||||
// Time Complexity: O(n)
|
||||
// Space Complexity: O(n)
|
||||
// Allocations: 1 set, n elements
|
||||
func NewSet[T comparable](slice []T) Set[T] {
|
||||
set := make(Set[T])
|
||||
for _, el := range slice {
|
||||
set[el] = struct{}{}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Initialize a new set while preserving the elements that were found to be duplicate.
|
||||
// The duplicate slice will be nil if there are no duplicates to save on allocations.
|
||||
//
|
||||
// Time Complexity: O(n)
|
||||
// Space Complexity: O(n)
|
||||
// Allocations (best case): 1 set, n elements
|
||||
// Allocations (worst case): 1 set, n/2 elements, 2 slices, one with n-1 space, the
|
||||
// other is the first one resized based on number of duplicates.
|
||||
func NewSetSaveDuplicates[T comparable](slice []T) (Set[T], []T) {
|
||||
set := make(Set[T])
|
||||
var dupes []T = nil
|
||||
dupesIdx := 0
|
||||
for _, el := range slice {
|
||||
if set.Contains(el) {
|
||||
if dupes == nil {
|
||||
// Now that we know there is at least one dupe, we can make the allocation.
|
||||
// The max possible size of the duplicate array is n-1 (aka a slice where
|
||||
// every element is the same).
|
||||
dupes = make([]T, len(slice)-1)
|
||||
}
|
||||
dupes[dupesIdx] = el
|
||||
dupesIdx++
|
||||
} else {
|
||||
set[el] = struct{}{}
|
||||
}
|
||||
}
|
||||
return set, dupes[:dupesIdx]
|
||||
}
|
||||
|
||||
// ToSlice will take all of the elements of the set and create a new slice out of them.
|
||||
//
|
||||
// Time Complexity: O(n)
|
||||
// Space Complexity: O(n)
|
||||
// Allocations: 1 slice, n elements
|
||||
func (set Set[T]) ToSlice() []T {
|
||||
slice := make([]T, len(set))
|
||||
i := 0
|
||||
for el := range set {
|
||||
slice[i] = el
|
||||
i++
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
// Add an element to the set.
|
||||
//
|
||||
// Time Complexity: O(1)
|
||||
// Space Complexity: O(1)
|
||||
// Allocations: 1 elements
|
||||
func (s Set[T]) Add(el T) {
|
||||
s[el] = struct{}{}
|
||||
}
|
||||
|
||||
// Remove an element from the set.
|
||||
//
|
||||
// Time Complexity: O(1)
|
||||
// Space Complexity: O(1)
|
||||
// Allocations: None
|
||||
func (s Set[T]) Remove(el T) {
|
||||
delete(s, el)
|
||||
}
|
||||
|
||||
// Check if the set contains a particular value.
|
||||
//
|
||||
// Time Complexity: O(1)
|
||||
// Space Complexity: O(1)
|
||||
// Allocations: None
|
||||
func (s Set[T]) Contains(el T) bool {
|
||||
_, containsEl := s[el]
|
||||
return containsEl
|
||||
}
|
||||
|
||||
// Creates a Clone of the set which contains all the same values.
|
||||
//
|
||||
// Time Complexity: O(n)
|
||||
// Space Complexity: O(n)
|
||||
// Allocations: 1 set, n elements
|
||||
func (s Set[T]) Clone() Set[T] {
|
||||
clone := make(Set[T])
|
||||
for el := range s {
|
||||
clone.Add(el)
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
// Checks if the set is equal to another.
|
||||
//
|
||||
// Time Complexity: O(n)
|
||||
// Space Complexity: O(1)
|
||||
// Allocations: None
|
||||
func (s Set[T]) Equals(s2 Set[T]) bool {
|
||||
if len(s) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for el := range s {
|
||||
if !s2.Contains(el) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package collections_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/RageCage64/collections-go"
|
||||
"github.com/RageCage64/go-assert"
|
||||
)
|
||||
|
||||
func TestSetToSlice(t *testing.T) {
|
||||
set := collections.Set[int]{
|
||||
1: {},
|
||||
2: {},
|
||||
3: {},
|
||||
}
|
||||
slice := set.ToSlice()
|
||||
assert.SliceEqual(t, []int{1, 2, 3}, slice)
|
||||
}
|
||||
|
||||
func runNewSetSaveDuplicatesTestCase[T comparable](
|
||||
t *testing.T,
|
||||
slice []T,
|
||||
expectedSet collections.Set[T],
|
||||
expectedDupes []T,
|
||||
) {
|
||||
t.Helper()
|
||||
newSet, dupes := collections.NewSetSaveDuplicates(slice)
|
||||
assert.Assert(
|
||||
t,
|
||||
expectedSet.Equals(newSet),
|
||||
"new set does not equal expectation:\nexpected: %v\ngot: %v",
|
||||
expectedSet,
|
||||
newSet,
|
||||
)
|
||||
assert.SliceEqual(t, expectedDupes, dupes)
|
||||
}
|
||||
|
||||
func TestNewSetSaveDuplicates(t *testing.T) {
|
||||
t.Run("no duplicates", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runNewSetSaveDuplicatesTestCase(
|
||||
t,
|
||||
[]int{1, 2, 3},
|
||||
collections.Set[int]{
|
||||
1: {},
|
||||
2: {},
|
||||
3: {},
|
||||
},
|
||||
[]int{},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("all duplicates", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runNewSetSaveDuplicatesTestCase(
|
||||
t,
|
||||
[]int{1, 1, 2, 2, 3, 3},
|
||||
collections.Set[int]{
|
||||
1: {},
|
||||
2: {},
|
||||
3: {},
|
||||
},
|
||||
[]int{1, 2, 3},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("all elements the same", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runNewSetSaveDuplicatesTestCase(
|
||||
t,
|
||||
[]int{1, 1, 1, 1},
|
||||
collections.Set[int]{
|
||||
1: {},
|
||||
},
|
||||
[]int{1, 1, 1},
|
||||
)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue