Skip to content

Commit df0c692

Browse files
authored
Merge branch 'main' into feature/marshal-bson
2 parents fff59c0 + 9480c3e commit df0c692

File tree

9 files changed

+476
-99
lines changed

9 files changed

+476
-99
lines changed

.github/workflows/ci.yml

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,18 @@ jobs:
44
test:
55
strategy:
66
matrix:
7-
go-version: [1.20.1, 1.21.1]
7+
go-version: [1.20.x, 1.21.x, 1.22.x, 1.23.x, 1.24.x]
88
os: [ubuntu-latest, macos-latest, windows-latest]
99
runs-on: ${{ matrix.os }}
1010
steps:
11-
- name: Install Go
12-
uses: actions/setup-go@v3
13-
with:
14-
go-version: ${{ matrix.go-version }}
15-
stable: false
16-
- name: Checkout code
17-
uses: actions/checkout@v3
18-
- name: Test
19-
run: |
20-
go test -v -race ./...
21-
# go vet ./...
22-
# go test -bench=.
11+
- name: Install Go
12+
uses: actions/setup-go@v5
13+
with:
14+
go-version: ${{ matrix.go-version }}
15+
- name: Checkout code
16+
uses: actions/checkout@v4
17+
- name: Test
18+
run: |
19+
go test -v -race ./...
20+
# go vet ./...
21+
# go test -bench=.

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ The missing `generic` set collection for the Go language. Until Go has sets bui
99
## Psst
1010
* Hi there, 👋! Do you use or have interest in the [Zig programming language](https://ziglang.org/) created by Andrew Kelley? If so, the golang-set project has a new sibling project: [ziglang-set](https://github.com/deckarep/ziglang-set)! Come check it out!
1111

12+
## Update 3/14/2025
13+
* Packaged version: `2.8.0` introduces support for true iterators for Go 1.23+. Please see [issue #141](https://github.com/deckarep/golang-set/issues/141)
14+
for further details on the implications of how iterations work between older Go versions vs newer Go versions. Additionally, this
15+
release has a minor unit-test spelling fix.
16+
17+
## Update 12/3/2024
18+
* Packaged version: `2.7.0` fixes a long-standing bug with *JSON Unmarshaling*. A large refactor in the interest of performance
19+
introduced this bug and there was no way around it but to revert the code back to how it was previously. The performance
20+
difference was likely negligible to begin with. JSON Marshaling and Unmarshaling is now properly supported again without
21+
needing to do workarounds.
22+
1223
## Update 3/5/2023
1324
* Packaged version: `2.2.0` release includes a refactor to minimize pointer indirection, better method documentation standards and a few constructor convenience methods to increase ergonomics when appending items `Append` or creating a new set from an exist `Map`.
1425
* supports `new generic` syntax

set.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ type Set[T comparable] interface {
7575
// given items are in the set.
7676
ContainsAny(val ...T) bool
7777

78+
// ContainsAnyElement returns whether at least one of the
79+
// given element are in the set.
80+
ContainsAnyElement(other Set[T]) bool
81+
7882
// Difference returns the difference between this set
7983
// and other. The returned set will contain
8084
// all elements of this set that are not also
@@ -262,3 +266,13 @@ func NewThreadUnsafeSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] {
262266

263267
return s
264268
}
269+
270+
// Elements returns an iterator that yields the elements of the set. Starting
271+
// with Go 1.23, users can use a for loop to iterate over it.
272+
func Elements[T comparable](s Set[T]) func(func(element T) bool) {
273+
return func(yield func(element T) bool) {
274+
s.Each(func(t T) bool {
275+
return !yield(t)
276+
})
277+
}
278+
}

set123_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//go:build go1.23
2+
// +build go1.23
3+
4+
package mapset
5+
6+
import (
7+
"testing"
8+
)
9+
10+
func Test_Elements123(t *testing.T) {
11+
a := NewSet[string]()
12+
13+
a.Add("Z")
14+
a.Add("Y")
15+
a.Add("X")
16+
a.Add("W")
17+
18+
b := NewSet[string]()
19+
for elem := range Elements(a) {
20+
b.Add(elem)
21+
}
22+
23+
if !a.Equal(b) {
24+
t.Error("The sets are not equal after iterating (Each) through the first set")
25+
}
26+
27+
var count int
28+
for range Elements(a) {
29+
if count == 2 {
30+
break
31+
}
32+
count++
33+
}
34+
35+
if count != 2 {
36+
t.Error("Iteration should stop on the way")
37+
}
38+
}

set_test.go

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,33 @@ func Test_ContainsAnySet(t *testing.T) {
398398
}
399399
}
400400

401+
func Test_ContainsAnyElement(t *testing.T) {
402+
a := NewSet[int]()
403+
a.Add(1)
404+
a.Add(3)
405+
a.Add(5)
406+
407+
b := NewSet[int]()
408+
a.Add(2)
409+
a.Add(4)
410+
a.Add(6)
411+
412+
if ret := a.ContainsAnyElement(b); ret {
413+
t.Errorf("set a not contain any element in set b")
414+
}
415+
416+
a.Add(10)
417+
418+
if ret := a.ContainsAnyElement(b); ret {
419+
t.Errorf("set a not contain any element in set b")
420+
}
421+
422+
b.Add(10)
423+
424+
if ret := a.ContainsAnyElement(b); !ret {
425+
t.Errorf("set a contain 10")
426+
}
427+
}
401428
func Test_ClearSet(t *testing.T) {
402429
a := makeSetInt([]int{2, 5, 9, 10})
403430

@@ -1248,7 +1275,7 @@ func Test_EmptySetProperties(t *testing.T) {
12481275

12491276
c = a.Intersect(empty)
12501277
if !c.Equal(empty) {
1251-
t.Error("The intesection of any set with the empty set is supposed to be the empty set")
1278+
t.Error("The intersection of any set with the empty set is supposed to be the empty set")
12521279
}
12531280

12541281
if empty.Cardinality() != 0 {
@@ -1346,6 +1373,38 @@ func Test_NewThreadUnsafeSetFromMapKey_Strings(t *testing.T) {
13461373
}
13471374
}
13481375

1376+
func Test_Elements(t *testing.T) {
1377+
a := NewSet[string]()
1378+
1379+
a.Add("Z")
1380+
a.Add("Y")
1381+
a.Add("X")
1382+
a.Add("W")
1383+
1384+
b := NewSet[string]()
1385+
Elements(a)(func(elem string) bool {
1386+
b.Add(elem)
1387+
return true
1388+
})
1389+
1390+
if !a.Equal(b) {
1391+
t.Error("The sets are not equal after iterating (Each) through the first set")
1392+
}
1393+
1394+
var count int
1395+
Elements(a)(func(elem string) bool {
1396+
if count == 2 {
1397+
return false
1398+
}
1399+
count++
1400+
return true
1401+
})
1402+
1403+
if count != 2 {
1404+
t.Error("Iteration should stop on the way")
1405+
}
1406+
}
1407+
13491408
func Test_Example(t *testing.T) {
13501409
/*
13511410
requiredClasses := NewSet()

threadsafe.go

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import (
3333

3434
type threadSafeSet[T comparable] struct {
3535
sync.RWMutex
36-
uss threadUnsafeSet[T]
36+
uss *threadUnsafeSet[T]
3737
}
3838

3939
func newThreadSafeSet[T comparable]() *threadSafeSet[T] {
@@ -86,6 +86,19 @@ func (t *threadSafeSet[T]) ContainsAny(v ...T) bool {
8686
return ret
8787
}
8888

89+
func (t *threadSafeSet[T]) ContainsAnyElement(other Set[T]) bool {
90+
o := other.(*threadSafeSet[T])
91+
92+
t.RLock()
93+
o.RLock()
94+
95+
ret := t.uss.ContainsAnyElement(o.uss)
96+
97+
t.RUnlock()
98+
o.RUnlock()
99+
return ret
100+
}
101+
89102
func (t *threadSafeSet[T]) IsEmpty() bool {
90103
return t.Cardinality() == 0
91104
}
@@ -127,7 +140,7 @@ func (t *threadSafeSet[T]) Union(other Set[T]) Set[T] {
127140
t.RLock()
128141
o.RLock()
129142

130-
unsafeUnion := t.uss.Union(o.uss).(threadUnsafeSet[T])
143+
unsafeUnion := t.uss.Union(o.uss).(*threadUnsafeSet[T])
131144
ret := &threadSafeSet[T]{uss: unsafeUnion}
132145
t.RUnlock()
133146
o.RUnlock()
@@ -140,7 +153,7 @@ func (t *threadSafeSet[T]) Intersect(other Set[T]) Set[T] {
140153
t.RLock()
141154
o.RLock()
142155

143-
unsafeIntersection := t.uss.Intersect(o.uss).(threadUnsafeSet[T])
156+
unsafeIntersection := t.uss.Intersect(o.uss).(*threadUnsafeSet[T])
144157
ret := &threadSafeSet[T]{uss: unsafeIntersection}
145158
t.RUnlock()
146159
o.RUnlock()
@@ -153,7 +166,7 @@ func (t *threadSafeSet[T]) Difference(other Set[T]) Set[T] {
153166
t.RLock()
154167
o.RLock()
155168

156-
unsafeDifference := t.uss.Difference(o.uss).(threadUnsafeSet[T])
169+
unsafeDifference := t.uss.Difference(o.uss).(*threadUnsafeSet[T])
157170
ret := &threadSafeSet[T]{uss: unsafeDifference}
158171
t.RUnlock()
159172
o.RUnlock()
@@ -166,7 +179,7 @@ func (t *threadSafeSet[T]) SymmetricDifference(other Set[T]) Set[T] {
166179
t.RLock()
167180
o.RLock()
168181

169-
unsafeDifference := t.uss.SymmetricDifference(o.uss).(threadUnsafeSet[T])
182+
unsafeDifference := t.uss.SymmetricDifference(o.uss).(*threadUnsafeSet[T])
170183
ret := &threadSafeSet[T]{uss: unsafeDifference}
171184
t.RUnlock()
172185
o.RUnlock()
@@ -181,7 +194,7 @@ func (t *threadSafeSet[T]) Clear() {
181194

182195
func (t *threadSafeSet[T]) Remove(v T) {
183196
t.Lock()
184-
delete(t.uss, v)
197+
delete(*t.uss, v)
185198
t.Unlock()
186199
}
187200

@@ -194,25 +207,25 @@ func (t *threadSafeSet[T]) RemoveAll(i ...T) {
194207
func (t *threadSafeSet[T]) Cardinality() int {
195208
t.RLock()
196209
defer t.RUnlock()
197-
return len(t.uss)
210+
return len(*t.uss)
198211
}
199212

200213
func (t *threadSafeSet[T]) Each(cb func(T) bool) {
201214
t.RLock()
202-
for elem := range t.uss {
215+
defer t.RUnlock()
216+
for elem := range *t.uss {
203217
if cb(elem) {
204218
break
205219
}
206220
}
207-
t.RUnlock()
208221
}
209222

210223
func (t *threadSafeSet[T]) Iter() <-chan T {
211224
ch := make(chan T)
212225
go func() {
213226
t.RLock()
214227

215-
for elem := range t.uss {
228+
for elem := range *t.uss {
216229
ch <- elem
217230
}
218231
close(ch)
@@ -228,7 +241,7 @@ func (t *threadSafeSet[T]) Iterator() *Iterator[T] {
228241
go func() {
229242
t.RLock()
230243
L:
231-
for elem := range t.uss {
244+
for elem := range *t.uss {
232245
select {
233246
case <-stopCh:
234247
break L
@@ -257,7 +270,7 @@ func (t *threadSafeSet[T]) Equal(other Set[T]) bool {
257270
func (t *threadSafeSet[T]) Clone() Set[T] {
258271
t.RLock()
259272

260-
unsafeClone := t.uss.Clone().(threadUnsafeSet[T])
273+
unsafeClone := t.uss.Clone().(*threadUnsafeSet[T])
261274
ret := &threadSafeSet[T]{uss: unsafeClone}
262275
t.RUnlock()
263276
return ret
@@ -277,9 +290,10 @@ func (t *threadSafeSet[T]) Pop() (T, bool) {
277290
}
278291

279292
func (t *threadSafeSet[T]) ToSlice() []T {
280-
keys := make([]T, 0, t.Cardinality())
281293
t.RLock()
282-
for elem := range t.uss {
294+
l := len(*t.uss)
295+
keys := make([]T, 0, l)
296+
for elem := range *t.uss {
283297
keys = append(keys, elem)
284298
}
285299
t.RUnlock()

0 commit comments

Comments
 (0)