mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
3 Commits
6c7f38d8b3
...
5db1d07d6d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5db1d07d6d | ||
|
|
6c6d14828a | ||
|
|
0e1593c67b |
@@ -4,19 +4,21 @@
|
||||
// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate.
|
||||
package datastructure
|
||||
|
||||
import "sort"
|
||||
|
||||
// Set is a data container, like slice, but element of set is not duplicate.
|
||||
type Set[T comparable] map[T]struct{}
|
||||
|
||||
// New create a instance of set from given values.
|
||||
func New[T comparable](items ...T) Set[T] {
|
||||
set := make(Set[T])
|
||||
set := make(Set[T], len(items))
|
||||
set.Add(items...)
|
||||
return set
|
||||
}
|
||||
|
||||
// FromSlice create a set from given slice.
|
||||
func FromSlice[T comparable](items []T) Set[T] {
|
||||
set := make(Set[T])
|
||||
set := make(Set[T], len(items))
|
||||
for _, item := range items {
|
||||
set.Add(item)
|
||||
}
|
||||
@@ -77,8 +79,7 @@ func (s Set[T]) ContainAll(other Set[T]) bool {
|
||||
|
||||
// Clone return a copy of set
|
||||
func (s Set[T]) Clone() Set[T] {
|
||||
set := New[T]()
|
||||
set.Add(s.Values()...)
|
||||
set := FromSlice(s.ToSlice())
|
||||
return set
|
||||
}
|
||||
|
||||
@@ -116,14 +117,11 @@ func (s Set[T]) Size() int {
|
||||
}
|
||||
|
||||
// Values return all values of set
|
||||
// Deprecated: Values function is deprecated and will be removed in future versions. Please use ToSlice() function instead.
|
||||
//
|
||||
// The ToSlice() function provides the same functionality as Values and returns a slice containing all values of the set.
|
||||
func (s Set[T]) Values() []T {
|
||||
result := make([]T, 0, len(s))
|
||||
|
||||
s.Iterate(func(value T) {
|
||||
result = append(result, value)
|
||||
})
|
||||
|
||||
return result
|
||||
return s.ToSlice()
|
||||
}
|
||||
|
||||
// Union creates a new set contain all element of set s and other
|
||||
@@ -163,7 +161,7 @@ func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
|
||||
return set
|
||||
}
|
||||
|
||||
// Minus creates an set of whose element in origin set but not in compared set
|
||||
// Minus creates a set of whose element in origin set but not in compared set
|
||||
func (s Set[T]) Minus(comparedSet Set[T]) Set[T] {
|
||||
set := New[T]()
|
||||
|
||||
@@ -197,3 +195,25 @@ func (s Set[T]) Pop() (v T, ok bool) {
|
||||
|
||||
return v, false
|
||||
}
|
||||
|
||||
// ToSlice returns a slice containing all values of the set.
|
||||
func (s Set[T]) ToSlice() []T {
|
||||
if s.IsEmpty() {
|
||||
return []T{}
|
||||
}
|
||||
result := make([]T, 0, s.Size())
|
||||
s.Iterate(func(value T) {
|
||||
result = append(result, value)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ToSortedSlice returns a sorted slice containing all values of the set.
|
||||
func (s Set[T]) ToSortedSlice(less func(v1, v2 T) bool) []T {
|
||||
result := s.ToSlice()
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return less(result[i], result[j])
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
@@ -260,3 +262,65 @@ func TestEachWithBreak(t *testing.T) {
|
||||
// assert.Equal(3, val)
|
||||
// assert.Equal(true, ok)
|
||||
// }
|
||||
|
||||
func TestSet_ToSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_ToSlice")
|
||||
|
||||
set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1})
|
||||
set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0})
|
||||
set3 := New[string]()
|
||||
|
||||
slice1 := set1.ToSlice()
|
||||
slice2 := set2.ToSlice()
|
||||
slice3 := set3.ToSlice()
|
||||
|
||||
sort.Ints(slice1)
|
||||
sort.Float64s(slice2)
|
||||
|
||||
assert.Equal(5, len(slice1))
|
||||
assert.Equal(4, len(slice2))
|
||||
assert.Equal(0, len(slice3))
|
||||
|
||||
assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7}))
|
||||
assert.Equal(true, reflect.DeepEqual(slice2, []float64{-2.65, 0, 1.11, 4.25}))
|
||||
assert.Equal("[]string", reflect.TypeOf(slice3).String())
|
||||
}
|
||||
|
||||
func TestSet_ToSortedSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_ToSortedSlice")
|
||||
|
||||
set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1})
|
||||
set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0})
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
set3 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
|
||||
|
||||
slice1 := set1.ToSortedSlice(func(v1, v2 int) bool {
|
||||
return v1 < v2
|
||||
})
|
||||
slice2 := set2.ToSortedSlice(func(v1, v2 float64) bool {
|
||||
return v2 < v1
|
||||
})
|
||||
slice3 := set3.ToSortedSlice(func(v1, v2 Person) bool {
|
||||
return v1.Age < v2.Age
|
||||
})
|
||||
|
||||
assert.Equal(5, len(slice1))
|
||||
assert.Equal(4, len(slice2))
|
||||
assert.Equal(3, len(slice3))
|
||||
|
||||
assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7}))
|
||||
assert.Equal(true, reflect.DeepEqual(slice2, []float64{4.25, 1.11, 0, -2.65}))
|
||||
assert.Equal(true, reflect.DeepEqual(slice3, []Person{
|
||||
{"Jerry", 18},
|
||||
{"Tom", 20},
|
||||
{"Spike", 25},
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ import (
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
- [Pop](#Pop)
|
||||
- [ToSlice](#ToSlice)
|
||||
- [ToSortedSlice](#ToSortedSlice)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -52,7 +55,7 @@ import (
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type Set[T comparable] map[T]bool
|
||||
type Set[T comparable] map[T]struct{}
|
||||
func New[T comparable](items ...T) Set[T]
|
||||
```
|
||||
|
||||
@@ -98,9 +101,10 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Values">Values</span>
|
||||
### <span id="Values">Values<sup>deprecated</sup></span>
|
||||
|
||||
<p>获取集合中所有元素的切片</p>
|
||||
<p>获取集合中所有元素的切片<br>
|
||||
<a href='#ToSlice'>ToSlice()</a> 方法提供与 Values 方法相同的功能</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -647,3 +651,58 @@ func main() {
|
||||
fmt.Println(ok) // true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToSlice">ToSlice</span>
|
||||
|
||||
<p>以切片的形式返回集合中所有的元素(无序)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) ToSlice() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
s := set.New(1, 2, 3, 4, 5)
|
||||
|
||||
val := s.ToSlice()
|
||||
fmt.Println(val) // [2 3 4 5 1]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToSortedSlice">ToSortedSlice</span>
|
||||
|
||||
<p>以切片的形式返回集合中所有的元素(按给定的规则排序)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) ToSortedSlice() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
s1 := set.New(1, 2, 3, 4, 5)
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
|
||||
|
||||
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
|
||||
return v1 < v2
|
||||
})
|
||||
|
||||
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
|
||||
return v1.Age < v2.Age
|
||||
})
|
||||
|
||||
fmt.Println(res1) // [1 2 3 4 5]
|
||||
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -41,6 +41,9 @@ import (
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
- [Pop](#Pop)
|
||||
- [ToSlice](#ToSlice)
|
||||
- [ToSortedSlice](#ToSortedSlice)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -53,7 +56,7 @@ import (
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type Set[T comparable] map[T]bool
|
||||
type Set[T comparable] map[T]struct{}
|
||||
func New[T comparable](items ...T) Set[T]
|
||||
```
|
||||
|
||||
@@ -99,9 +102,10 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Values">Values</span>
|
||||
### <span id="Values">Values<sup>deprecated</sup></span>
|
||||
|
||||
<p>Return slice of all set data</p>
|
||||
<p>Return slice of all set data.<br>
|
||||
The <a href='#ToSlice'>ToSlice()</a> function provides the same functionality as Values and returns a slice containing all values of the set.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
@@ -648,3 +652,58 @@ func main() {
|
||||
fmt.Println(ok) // true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToSlice">ToSlice</span>
|
||||
|
||||
<p>returns a slice containing all values of the set.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) ToSlice() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
s := set.New(1, 2, 3, 4, 5)
|
||||
|
||||
val := s.ToSlice()
|
||||
fmt.Println(val) // [2 3 4 5 1]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToSortedSlice">ToSortedSlice</span>
|
||||
|
||||
<p>returns a sorted slice containing all values of the set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) ToSortedSlice() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
s1 := set.New(1, 2, 3, 4, 5)
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
|
||||
|
||||
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
|
||||
return v1 < v2
|
||||
})
|
||||
|
||||
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
|
||||
return v1.Age < v2.Age
|
||||
})
|
||||
|
||||
fmt.Println(res1) // [1 2 3 4 5]
|
||||
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1170,6 +1170,21 @@ func AppendIfAbsent[T comparable](slice []T, item T) []T {
|
||||
return slice
|
||||
}
|
||||
|
||||
// SetToDefaultIf sets elements to their default value if they match the given predicate.
|
||||
// It retains the positions of the elements in the slice.
|
||||
// It returns slice of T and the count of modified slice items
|
||||
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int) {
|
||||
var count int
|
||||
for i := 0; i < len(slice); i++ {
|
||||
if predicate(slice[i]) {
|
||||
var zeroValue T
|
||||
slice[i] = zeroValue
|
||||
count++
|
||||
}
|
||||
}
|
||||
return slice, count
|
||||
}
|
||||
|
||||
// KeyBy converts a slice to a map based on a callback function.
|
||||
// Play: https://go.dev/play/p/uXod2LWD1Kg
|
||||
func KeyBy[T any, U comparable](slice []T, iteratee func(item T) U) map[U]T {
|
||||
|
||||
@@ -1102,3 +1102,13 @@ func ExampleRandom() {
|
||||
// Output:
|
||||
// okk
|
||||
}
|
||||
|
||||
func ExampleSetToDefaultIf() {
|
||||
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||
modifiedStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||
fmt.Println(modifiedStrs)
|
||||
fmt.Println(count)
|
||||
// Output:
|
||||
// [ b c d ]
|
||||
// 3
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package slice
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@@ -1218,3 +1219,141 @@ func TestRandom(t *testing.T) {
|
||||
assert.Greater(len(arr), idx)
|
||||
assert.Equal(arr[idx], val)
|
||||
}
|
||||
|
||||
func TestSetToDefaultIf(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "SetToDefaultIf")
|
||||
|
||||
// Subtest for strings
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||
actualStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||
assert.Equal([]string{"", "b", "", "c", "d", ""}, actualStrs)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for integers
|
||||
t.Run("integers", func(t *testing.T) {
|
||||
ints := []int{1, 2, 3, 2, 4, 2}
|
||||
actualInts, count := SetToDefaultIf(ints, func(i int) bool { return i == 2 })
|
||||
assert.Equal([]int{1, 0, 3, 0, 4, 0}, actualInts)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for floating-point numbers
|
||||
t.Run("floats", func(t *testing.T) {
|
||||
floats := []float64{1.1, 2.2, 3.3, 2.2, 4.4, 2.2}
|
||||
actualFloats, count := SetToDefaultIf(floats, func(f float64) bool { return f == 2.2 })
|
||||
assert.Equal([]float64{1.1, 0, 3.3, 0, 4.4, 0}, actualFloats)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for booleans
|
||||
t.Run("booleans", func(t *testing.T) {
|
||||
bools := []bool{true, false, true, true, false}
|
||||
actualBools, count := SetToDefaultIf(bools, func(b bool) bool { return b })
|
||||
assert.Equal([]bool{false, false, false, false, false}, actualBools)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for a custom type
|
||||
type customType struct {
|
||||
field string
|
||||
}
|
||||
t.Run("customType", func(t *testing.T) {
|
||||
customs := []customType{{"a"}, {"b"}, {"a"}, {"c"}}
|
||||
actualCustoms, count := SetToDefaultIf(customs, func(c customType) bool { return c.field == "a" })
|
||||
expected := []customType{{""}, {"b"}, {""}, {"c"}}
|
||||
assert.Equal(expected, actualCustoms)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for slice of integers
|
||||
t.Run("sliceOfInts", func(t *testing.T) {
|
||||
sliceOfInts := [][]int{{1, 2}, {3, 4}, {5, 6}, {1, 2}}
|
||||
actualSlice, count := SetToDefaultIf(sliceOfInts, func(s []int) bool { return reflect.DeepEqual(s, []int{1, 2}) })
|
||||
expected := [][]int{nil, {3, 4}, {5, 6}, nil}
|
||||
assert.Equal(expected, actualSlice)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for maps (simple use case)
|
||||
t.Run("mapOfStringToInts", func(t *testing.T) {
|
||||
maps := []map[string]int{{"a": 1}, {"b": 2}, {"a": 1}, {"c": 3}}
|
||||
actualMaps, count := SetToDefaultIf(maps, func(m map[string]int) bool { _, ok := m["a"]; return ok })
|
||||
expected := []map[string]int{nil, {"b": 2}, nil, {"c": 3}}
|
||||
assert.Equal(expected, actualMaps)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for pointers to integers
|
||||
t.Run("pointersToInts", func(t *testing.T) {
|
||||
one, two, three := 1, 2, 3
|
||||
pointers := []*int{&one, &two, &one, &three}
|
||||
actualPointers, count := SetToDefaultIf(pointers, func(p *int) bool { return p != nil && *p == 1 })
|
||||
expected := []*int{nil, &two, nil, &three}
|
||||
assert.Equal(expected, actualPointers)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for channels
|
||||
t.Run("channels", func(t *testing.T) {
|
||||
ch1, ch2 := make(chan int), make(chan int)
|
||||
channels := []chan int{ch1, ch2, ch1}
|
||||
actualChannels, count := SetToDefaultIf(channels, func(ch chan int) bool { return ch == ch1 })
|
||||
expected := []chan int{nil, ch2, nil}
|
||||
assert.Equal(expected, actualChannels)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for interfaces
|
||||
t.Run("interfaces", func(t *testing.T) {
|
||||
var i1, i2 interface{} = "hello", 42
|
||||
interfaces := []interface{}{i1, i2, i1}
|
||||
actualInterfaces, count := SetToDefaultIf(interfaces, func(i interface{}) bool { _, ok := i.(string); return ok })
|
||||
expected := []interface{}{nil, 42, nil}
|
||||
assert.Equal(expected, actualInterfaces)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for complex structs
|
||||
t.Run("complexStructs", func(t *testing.T) {
|
||||
type ComplexStruct struct {
|
||||
Name string
|
||||
Value int
|
||||
Data []byte
|
||||
}
|
||||
cs1, cs2 := ComplexStruct{Name: "Test", Value: 1, Data: []byte{1, 2, 3}}, ComplexStruct{Name: "Another", Value: 2, Data: []byte{4, 5, 6}}
|
||||
complexStructs := []ComplexStruct{cs1, cs2, cs1}
|
||||
actualComplexStructs, count := SetToDefaultIf(complexStructs, func(cs ComplexStruct) bool { return cs.Name == "Test" })
|
||||
expected := []ComplexStruct{{}, cs2, {}}
|
||||
assert.Equal(expected, actualComplexStructs)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for uints
|
||||
t.Run("uints", func(t *testing.T) {
|
||||
uints := []uint{1, 2, 3, 2, 4, 2}
|
||||
actualUints, count := SetToDefaultIf(uints, func(u uint) bool { return u == 2 })
|
||||
assert.Equal([]uint{1, 0, 3, 0, 4, 0}, actualUints)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for float32
|
||||
t.Run("float32s", func(t *testing.T) {
|
||||
floats := []float32{1.1, 2.2, 3.3, 2.2, 4.4, 2.2}
|
||||
actualFloats, count := SetToDefaultIf(floats, func(f float32) bool { return f == 2.2 })
|
||||
assert.Equal([]float32{1.1, 0, 3.3, 0, 4.4, 0}, actualFloats)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for []byte
|
||||
t.Run("byteSlices", func(t *testing.T) {
|
||||
bytes := [][]byte{{'a', 'b'}, {'c', 'd'}, {'a', 'b'}}
|
||||
actualBytes, count := SetToDefaultIf(bytes, func(b []byte) bool { return string(b) == "ab" })
|
||||
expected := [][]byte{nil, {'c', 'd'}, nil}
|
||||
assert.Equal(expected, actualBytes)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user