Skip to content

Commit

Permalink
add Set.Pop and Set.Random
Browse files Browse the repository at this point in the history
  • Loading branch information
ayonli committed Sep 25, 2023
1 parent ba6aba9 commit a107db3
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 14 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ expired, and update the cache in the background.
Additional functions for dealing with maps.
- **[structx](https://pkg.go.dev/github.com/ayonli/goext/structx)** (Since v0.3.0)
Functions used to manipulate structs.
- **[number](https://pkg.go.dev/github.com/ayonli/goext/number)** (Since v0.4.0)
Functions for dealing with numbers.
- **[oop](https://pkg.go.dev/github.com/ayonli/goext/oop)**
Object-oriented abstract wrappers for basic data structures.
- `String` is an object-oriented abstract that works around multi-byte strings.
Expand Down
61 changes: 61 additions & 0 deletions collections/Set.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"strings"

"github.com/ayonli/goext/number"
)

// Set is an object-oriented collection that stores unique items and is thread-safe.
Expand Down Expand Up @@ -40,6 +42,65 @@ func (self *Set[T]) Delete(item T) bool {
return self.m.Delete(item)
}

// Removes and returns the first (if `order >= 0`) or the last (if `order < 0`) item from the set.
func (self *Set[T]) Pop(order int) (T, bool) {
if self.Size() == 0 {
return *new(T), false
}

self.m.mut.Lock()
defer self.m.mut.Unlock()

if order >= 0 {
for idx, item := range self.m.records {
if !item.Deleted {
self.m.deleteAt(idx)
return item.Key, true
}
}
} else {
for idx := len(self.m.records) - 1; idx >= 0; idx-- {
item := self.m.records[idx]

if !item.Deleted {
self.m.deleteAt(idx)
return item.Key, true
}
}
}

// never
return *new(T), false
}

// Removes and returns a random item from the set.
func (self *Set[T]) Random() (T, bool) {
if self.Size() == 0 {
return *new(T), false
}

self.m.mut.Lock()
defer self.m.mut.Unlock()

pos := number.Random(0, self.Size()-1)
idx := 0

for i, item := range self.m.records {
if !item.Deleted {
if idx == pos {
self.m.deleteAt(i)
return item.Key, true
}

idx++
}
}

// never
return *new(T), true
}

// Empties the set and resets its size.
func (self *Set[T]) Clear() {
self.m.Clear()
}
Expand Down
67 changes: 53 additions & 14 deletions collections/Set_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package collections_test
import (
"encoding/json"
"fmt"
"slices"

"github.com/ayonli/goext/collections"
)
Expand All @@ -25,10 +26,10 @@ func ExampleSet_json() {

s2 := &collections.Set[string]{}
json.Unmarshal(data, s2)
fmt.Println(s2)
fmt.Printf("%#v", s2)
// Output:
// ["Hello","World"]
// &collections.Set[Hello World]
// &collections.Set[string]{"Hello", "World"}
}

func ExampleNewSet() {
Expand Down Expand Up @@ -56,8 +57,7 @@ func ExampleSet_Add() {
}

func ExampleSet_Has() {
s := collections.NewSet([]string{})
s.Add("Hello")
s := collections.NewSet([]string{"Hello"})

fmt.Println(s.Has("Hello"))
fmt.Println(s.Has("World"))
Expand All @@ -67,8 +67,7 @@ func ExampleSet_Has() {
}

func ExampleSet_Delete() {
s := collections.NewSet([]string{})
s.Add("Hello").Add("World")
s := collections.NewSet([]string{"Hello", "World"})

ok1 := s.Delete("Hello") // succeed
ok2 := s.Delete("Hello") // failed
Expand All @@ -82,9 +81,52 @@ func ExampleSet_Delete() {
// false
}

func ExampleSet_Pop() {
s := collections.NewSet([]string{
"Hello",
"World",
"Hi",
"A-yon",
})

fmt.Println(s.Pop(0))
fmt.Println(s.Pop(-1))
fmt.Println(s.Size())

s.Clear()
fmt.Println(s.Pop(0))

// Output:
// Hello true
// A-yon true
// 2
// false
}

func ExampleSet_Random() {
list := []string{
"Hello",
"World",
"Hi",
"A-yon",
}
s := collections.NewSet(list)

item, _ := s.Random()
fmt.Println(slices.Contains(list, item))
fmt.Println(s.Size())

s.Clear()
fmt.Println(s.Random())

// Output:
// true
// 3
// false
}

func ExampleSet_Clear() {
s := collections.NewSet([]string{})
s.Add("Hello").Add("World")
s := collections.NewSet([]string{"Hello", "World"})

s.Clear()

Expand All @@ -94,17 +136,15 @@ func ExampleSet_Clear() {
}

func ExampleSet_Values() {
s := collections.NewSet([]string{})
s.Add("Hello").Add("World")
s := collections.NewSet([]string{"Hello", "World"})

fmt.Println(s.Values())
// Output:
// [Hello World]
}

func ExampleSet_ForEach() {
s := collections.NewSet([]string{})
s.Add("Hello").Add("World")
s := collections.NewSet([]string{"Hello", "World"})

s.ForEach(func(item string) {
fmt.Println(item)
Expand All @@ -115,8 +155,7 @@ func ExampleSet_ForEach() {
}

func ExampleSet_Size() {
s := collections.NewSet([]string{})
s.Add("Hello").Add("World")
s := collections.NewSet([]string{"Hello", "World"})

fmt.Println(s.Size())
// Output:
Expand Down
1 change: 1 addition & 0 deletions number/number.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Functions for dealing with numbers.
package number

import (
Expand Down

0 comments on commit a107db3

Please sign in to comment.