Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion safe_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func init() {
gob.Register(new(SafeMap[string, int]))
}

func TestNil(t *testing.T) {
func TestSafeMap_Nil(t *testing.T) {
var m SafeMap[string, int]

assert.False(t, m.Has("z"))
Expand Down
51 changes: 45 additions & 6 deletions set.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,17 @@ func NewSet[K comparable](values ...K) *Set[K] {

// Clear resets the set to an empty set
func (m *Set[K]) Clear() {
if m == nil {
return
}
m.items = nil
}

// Len returns the number of items in the set
func (m *Set[K]) Len() int {
if m == nil || m.items == nil {
return 0
}
return m.items.Len()
}

Expand All @@ -45,7 +51,7 @@ func (m *Set[K]) Len() int {
// While its safe to call methods of the set from within the Range function, its discouraged.
// If you ever switch to one of the SafeSet sets, it will cause a deadlock.
func (m *Set[K]) Range(f func(k K) bool) {
if m == nil || m.items == nil {
if m.Len() == 0 {
return
}
for k := range m.items {
Expand All @@ -57,22 +63,34 @@ func (m *Set[K]) Range(f func(k K) bool) {

// Has returns true if the value exists in the set.
func (m *Set[K]) Has(k K) bool {
if m.Len() == 0 {
return false
}
return m.items.Has(k)
}

// Delete removes the value from the set. If the value does not exist, nothing happens.
func (m *Set[K]) Delete(k K) {
if m.Len() == 0 {
return
}
m.items.Delete(k)
}

// Values returns a new slice containing the values of the set.
func (m *Set[K]) Values() []K {
if m.Len() == 0 {
return nil
}
return m.items.Keys()
}

// Add adds the value to the set.
// If the value already exists, nothing changes.
func (m *Set[K]) Add(k ...K) SetI[K] {
if m == nil {
panic("cannot add values to a nil Set")
}
if m.items == nil {
m.items = make(map[K]struct{})
}
Expand All @@ -90,6 +108,9 @@ func (m *Set[K]) Merge(in SetI[K]) {

// Copy adds the values from in to the set.
func (m *Set[K]) Copy(in SetI[K]) {
if m == nil {
panic("cannot copy to a nil Set")
}
if in == nil || in.Len() == 0 {
return
}
Expand All @@ -104,6 +125,9 @@ func (m *Set[K]) Copy(in SetI[K]) {

// Equal returns true if the two sets are the same length and contain the same values.
func (m *Set[K]) Equal(m2 SetI[K]) bool {
if m == nil {
return m2.Len() == 0
}
if m.Len() != m2.Len() {
return false
}
Expand Down Expand Up @@ -168,10 +192,12 @@ func (m *Set[K]) UnmarshalJSON(in []byte) (err error) {
// String returns the set as a string.
func (m *Set[K]) String() string {
ret := "{"
for i, v := range m.Values() {
ret += fmt.Sprintf("%#v", v)
if i < m.Len()-1 {
ret += ","
if m.Len() != 0 {
for i, v := range m.Values() {
ret += fmt.Sprintf("%#v", v)
if i < m.Len()-1 {
ret += ","
}
}
}
ret += "}"
Expand All @@ -180,12 +206,20 @@ func (m *Set[K]) String() string {

// All returns an iterator over all the items in the set. Order is not determinate.
func (m *Set[K]) All() iter.Seq[K] {
if m.Len() == 0 {
return func(yield func(K) bool) {
return
}
}
return m.items.KeysIter()
}

// Insert adds the values from seq to the map.
// Duplicates are overridden.
func (m *Set[K]) Insert(seq iter.Seq[K]) {
if m == nil {
panic("cannot insert into a nil Set")
}
if m.items == nil {
m.items = NewStdMap[K, struct{}]()
}
Expand All @@ -207,12 +241,17 @@ func CollectSet[K comparable](seq iter.Seq[K]) *Set[K] {
// the new keys and values are set using ordinary assignment.
func (m *Set[K]) Clone() *Set[K] {
m1 := NewSet[K]()
m1.items = m.items.Clone()
if m.Len() != 0 {
m1.items = m.items.Clone()
}
return m1
}

// DeleteFunc deletes any values for which del returns true.
func (m *Set[K]) DeleteFunc(del func(K) bool) {
if m.Len() == 0 {
return
}
del2 := func(k K, s struct{}) bool {
return del(k)
}
Expand Down
113 changes: 105 additions & 8 deletions set_ordered.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package maps
import (
"cmp"
"encoding/json"
"fmt"
"iter"
"slices"
)
Expand All @@ -25,9 +26,25 @@ func NewOrderedSet[K cmp.Ordered](values ...K) *OrderedSet[K] {
return s
}

// Clear resets the set to an empty set
func (m *OrderedSet[K]) Clear() {
if m == nil {
return
}
m.Set.Clear()
}

// Len returns the number of items in the set
func (m *OrderedSet[K]) Len() int {
if m == nil || m.items == nil {
return 0
}
return m.Set.Len()
}

// Range will range over the values in order.
func (m *OrderedSet[K]) Range(f func(k K) bool) {
if m == nil || m.items == nil {
if m.Len() == 0 {
return
}
values := m.Values()
Expand All @@ -38,13 +55,58 @@ func (m *OrderedSet[K]) Range(f func(k K) bool) {
}
}

// Values returns the values as a slice, in order.
// Has returns true if the value exists in the set.
func (m *OrderedSet[K]) Has(k K) bool {
if m.Len() == 0 {
return false
}
return m.Set.Has(k)
}

// Delete removes the value from the set. If the value does not exist, nothing happens.
func (m *OrderedSet[K]) Delete(k K) {
if m.Len() == 0 {
return
}
m.Set.Delete(k)
}

// Equal returns true if the two sets are the same length and contain the same values.
func (m *OrderedSet[K]) Equal(m2 SetI[K]) bool {
if m == nil {
return m2.Len() == 0
}
return m.Set.Equal(m2)
}

// Values returns a new slice containing the values of the set.
func (m *OrderedSet[K]) Values() []K {
if m.Len() == 0 {
return nil
}
v := m.items.Keys()
slices.Sort(v)
return v
}

// Add adds the value to the set.
// If the value already exists, nothing changes.
func (m *OrderedSet[K]) Add(k ...K) SetI[K] {
if m == nil {
panic("cannot add values to a nil Set")
}
m.Set.Add(k...)
return m
}

// Copy adds the values from in to the set.
func (m *OrderedSet[K]) Copy(in SetI[K]) {
if m == nil {
panic("cannot copy to a nil Set")
}
m.Set.Copy(in)
}

// MarshalJSON implements the json.Marshaler interface to convert the map into a JSON object.
func (m *OrderedSet[K]) MarshalJSON() (out []byte, err error) {
if m.Len() == 0 {
Expand All @@ -55,21 +117,56 @@ func (m *OrderedSet[K]) MarshalJSON() (out []byte, err error) {

// All returns an iterator over all the items in the set. Order is determinate.
func (m *OrderedSet[K]) All() iter.Seq[K] {
if m.Len() == 0 {
return func(yield func(K) bool) {
return
}
}
v := m.Values()
return slices.Values(v)
}

// Insert adds the values from seq to the map.
// Duplicates are overridden.
func (m *OrderedSet[K]) Insert(seq iter.Seq[K]) {
if m == nil {
panic("cannot insert into a nil Set")
}
m.Set.Insert(seq)
}

// Clone returns a copy of the Set. This is a shallow clone:
// the new keys and values are set using ordinary assignment.
func (m *OrderedSet[K]) Clone() *OrderedSet[K] {
m1 := NewOrderedSet[K]()
m1.items = m.items.Clone()
if m != nil {
m1.items = m.items.Clone()
}
return m1
}

// Add adds the value to the set.
// If the value already exists, nothing changes.
func (m *OrderedSet[K]) Add(k ...K) SetI[K] {
m.Set.Add(k...)
return m
// DeleteFunc deletes any values for which del returns true.
func (m *OrderedSet[K]) DeleteFunc(del func(K) bool) {
if m.Len() == 0 {
return
}
m.Set.DeleteFunc(del)
}

// String returns the set as a string.
func (m *OrderedSet[K]) String() string {
if m == nil {
return "{}"
}
ret := "{"
if m.Len() != 0 {
for i, v := range m.Values() {
ret += fmt.Sprintf("%#v", v)
if i < m.Len()-1 {
ret += ","
}
}
}
ret += "}"
return ret
}
56 changes: 53 additions & 3 deletions set_ordered_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ package maps
import (
"cmp"
"encoding/gob"
"fmt"
"github.com/stretchr/testify/assert"
"slices"
"testing"
)

type orderedSetT = OrderedSet[string]
type orderedSetTI = SetI[string]

func TestOrderedSet_SetI(t *testing.T) {
runSetITests[OrderedSet[string]](t, makeSetI[OrderedSet[string]])
runSetITests[orderedSetT](t, makeSetI[orderedSetT])
}

func init() {
gob.Register(new(OrderedSet[string]))
gob.Register(new(orderedSetT))
}

func TestOrderedSet_Values(t *testing.T) {
Expand Down Expand Up @@ -58,7 +60,7 @@ func TestOrderedSet_MarshalJSON(t *testing.T) {
}
}

func TestOrderedSetAll(t *testing.T) {
func TestOrderedSet_All(t *testing.T) {
set := NewOrderedSet[int]()
set.Add(5)
set.Add(3)
Expand Down Expand Up @@ -107,6 +109,54 @@ func TestOrderedSet_Clone(t *testing.T) {
m2 := m1.Clone()
assert.True(t, m1.Equal(m2))

var m3 *OrderedSet[string]
m4 := m3.Clone()
m3.Equal(m4)
assert.True(t, m3.Equal(m4))

m2.Add("d")
assert.False(t, m1.Equal(m2))
}

func TestOrderedSet_Nil(t *testing.T) {
t.Run("Nil", func(t *testing.T) {
var m1, m2 *OrderedSet[string]

assert.Equal(t, 0, m1.Len())
m1.Clear()
assert.True(t, m1.Equal(m2))
m3 := m2.Clone()
assert.True(t, m1.Equal(m3))
m3.Add("a")
assert.False(t, m1.Equal(m3))
m1.Range(func(k string) bool {
assert.Fail(t, "no range should happen")
return false
})
assert.False(t, m1.Has("b"))
m1.Delete("a")
assert.Empty(t, m1.Values())
assert.Equal(t, "{}", m1.String())
m1.DeleteFunc(func(k string) bool {
return false
})
for _ = range m1.All() {
assert.Fail(t, "no range should happen")
}
assert.Panics(t, func() {
m1.Insert(slices.Values([]string{"a"}))
})
assert.Panics(t, func() {
m1.Add("a")
})
assert.Panics(t, func() {
m1.Copy(m2)
})
})
}

func ExampleOrderedSet_String() {
m := NewOrderedSet("a", "c", "a", "b")
fmt.Print(m.String())
// Output: {"a","b","c"}
}
Loading
Loading