1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-03-01 00:35:28 +08:00

Compare commits

...

6 Commits

Author SHA1 Message Date
dudaodong 5a38e34063 Merge branch 'rc' of github.com:duke-git/lancet into rc 2024-08-30 16:55:10 +08:00
dudaodong ec092a009a test: we should write clean unit test code 2024-08-28 16:06:08 +08:00
dudaodong ca40b5d6c6 refactoring: rename SortByKeys to SortByKey 2024-08-28 10:58:14 +08:00
dudaodong a6d39a3bba feat: add OrderedMap for issue #237 2024-08-28 10:53:53 +08:00
dudaodong 38148978cf refactoring: change unit variable to package internal 2024-08-26 16:25:57 +08:00
dudaodong 3e8c3bd396 feat: add SortbyKeys for map 2024-08-23 11:21:29 +08:00
10 changed files with 1134 additions and 207 deletions
+38
View File
@@ -56,6 +56,7 @@ import (
- [ConcurrentMap_Has](#ConcurrentMap_Has) - [ConcurrentMap_Has](#ConcurrentMap_Has)
- [ConcurrentMap_Range](#ConcurrentMap_Range) - [ConcurrentMap_Range](#ConcurrentMap_Range)
- [GetOrSet](#GetOrSet) - [GetOrSet](#GetOrSet)
- [SortByKey](#SortByKey)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -1523,3 +1524,40 @@ func main() {
// b // b
} }
``` ```
### <span id="SortByKey">SortByKey</span>
<p>对传入的map根据key进行排序,返回排序后的map。</p>
<b>函数签名:</b>
```go
func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result := maputil.SortByKey(m)
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
```
+37
View File
@@ -1539,3 +1539,40 @@ func main() {
// b // b
} }
``` ```
### <span id="SortByKey">SortByKey</span>
<p>Sorts the map by its keys and returns a new map with sorted keys.</p>
<b>Signature:</b>
```go
func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
```
<b>Example:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result := maputil.SortByKey(m)
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
```
+42 -42
View File
@@ -18,63 +18,63 @@ import (
// http://en.wikipedia.org/wiki/Binary_prefix // http://en.wikipedia.org/wiki/Binary_prefix
const ( const (
// Decimal // Decimal
UnitB = 1 unitB = 1
UnitKB = 1000 unitKB = 1000
UnitMB = 1000 * UnitKB unitMB = 1000 * unitKB
UnitGB = 1000 * UnitMB unitGB = 1000 * unitMB
UnitTB = 1000 * UnitGB unitTB = 1000 * unitGB
UnitPB = 1000 * UnitTB unitPB = 1000 * unitTB
UnitEB = 1000 * UnitPB unitEB = 1000 * unitPB
// Binary // Binary
UnitBiB = 1 unitBiB = 1
UnitKiB = 1024 unitKiB = 1024
UnitMiB = 1024 * UnitKiB unitMiB = 1024 * unitKiB
UnitGiB = 1024 * UnitMiB unitGiB = 1024 * unitMiB
UnitTiB = 1024 * UnitGiB unitTiB = 1024 * unitGiB
UnitPiB = 1024 * UnitTiB unitPiB = 1024 * unitTiB
UnitEiB = 1024 * UnitPiB unitEiB = 1024 * unitPiB
) )
// type byteUnitMap map[byte]int64 // type byteUnitMap map[byte]int64
var ( var (
decimalByteMap = map[string]uint64{ decimalByteMap = map[string]uint64{
"b": UnitB, "b": unitB,
"kb": UnitKB, "kb": unitKB,
"mb": UnitMB, "mb": unitMB,
"gb": UnitGB, "gb": unitGB,
"tb": UnitTB, "tb": unitTB,
"pb": UnitPB, "pb": unitPB,
"eb": UnitEB, "eb": unitEB,
// Without suffix // Without suffix
"": UnitB, "": unitB,
"k": UnitKB, "k": unitKB,
"m": UnitMB, "m": unitMB,
"g": UnitGB, "g": unitGB,
"t": UnitTB, "t": unitTB,
"p": UnitPB, "p": unitPB,
"e": UnitEB, "e": unitEB,
} }
binaryByteMap = map[string]uint64{ binaryByteMap = map[string]uint64{
"bi": UnitBiB, "bi": unitBiB,
"kib": UnitKiB, "kib": unitKiB,
"mib": UnitMiB, "mib": unitMiB,
"gib": UnitGiB, "gib": unitGiB,
"tib": UnitTiB, "tib": unitTiB,
"pib": UnitPiB, "pib": unitPiB,
"eib": UnitEiB, "eib": unitEiB,
// Without suffix // Without suffix
"": UnitBiB, "": unitBiB,
"ki": UnitKiB, "ki": unitKiB,
"mi": UnitMiB, "mi": unitMiB,
"gi": UnitGiB, "gi": unitGiB,
"ti": UnitTiB, "ti": unitTiB,
"pi": UnitPiB, "pi": unitPiB,
"ei": UnitEiB, "ei": unitEiB,
} }
) )
+5 -5
View File
@@ -15,11 +15,11 @@ func TestDecimalBytes(t *testing.T) {
assert.Equal("1.024KB", DecimalBytes(1024)) assert.Equal("1.024KB", DecimalBytes(1024))
assert.Equal("1.2346MB", DecimalBytes(1234567)) assert.Equal("1.2346MB", DecimalBytes(1234567))
assert.Equal("1.235MB", DecimalBytes(1234567, 3)) assert.Equal("1.235MB", DecimalBytes(1234567, 3))
assert.Equal("1.123GB", DecimalBytes(float64(1.123*UnitGB))) assert.Equal("1.123GB", DecimalBytes(float64(1.123*unitGB)))
assert.Equal("2.123TB", DecimalBytes(float64(2.123*UnitTB))) assert.Equal("2.123TB", DecimalBytes(float64(2.123*unitTB)))
assert.Equal("3.123PB", DecimalBytes(float64(3.123*UnitPB))) assert.Equal("3.123PB", DecimalBytes(float64(3.123*unitPB)))
assert.Equal("4.123EB", DecimalBytes(float64(4.123*UnitEB))) assert.Equal("4.123EB", DecimalBytes(float64(4.123*unitEB)))
assert.Equal("1EB", DecimalBytes(float64(1000*UnitPB))) assert.Equal("1EB", DecimalBytes(float64(1000*unitPB)))
assert.Equal("62MB", DecimalBytes(61812496, 0)) assert.Equal("62MB", DecimalBytes(61812496, 0))
assert.Equal("61.8MB", DecimalBytes(61812496, 1)) assert.Equal("61.8MB", DecimalBytes(61812496, 1))
assert.Equal("401MB", DecimalBytes(401000000)) assert.Equal("401MB", DecimalBytes(401000000))
+20
View File
@@ -453,3 +453,23 @@ func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V {
return value return value
} }
// SortByKey sorts the map by its keys and returns a new map with sorted keys.
// Play: todo
func SortByKey[K constraints.Ordered, V any](m map[K]V, less func(a, b K) bool) (sortedKeysMap map[K]V) {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return less(keys[i], keys[j])
})
sortedKeysMap = make(map[K]V, len(m))
for _, k := range keys {
sortedKeysMap[k] = m[k]
}
return
}
+18
View File
@@ -540,3 +540,21 @@ func ExampleGetOrSet() {
// a // a
// b // b
} }
func ExampleSortByKey() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result := SortByKey(m, func(a, b int) bool {
return a < b
})
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
+44
View File
@@ -707,3 +707,47 @@ func TestGetOrSet(t *testing.T) {
assert.Equal("a", result1) assert.Equal("a", result1)
assert.Equal("b", result2) assert.Equal("b", result2)
} }
func TestSortByKey(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSortByKey")
m1 := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
expected1 := map[int]string{
1: "a",
2: "b",
3: "c",
4: "d",
}
result1 := SortByKey(m1, func(a, b int) bool {
return a < b
})
assert.Equal(expected1, result1)
m2 := map[string]int{
"c": 3,
"a": 1,
"d": 4,
"b": 2,
}
expected2 := map[string]int{
"d": 4,
"c": 3,
"b": 2,
"a": 1,
}
result2 := SortByKey(m2, func(a, b string) bool {
return a > b
})
assert.Equal(expected2, result2)
}
+431
View File
@@ -0,0 +1,431 @@
package maputil
import (
"container/list"
"encoding/json"
"fmt"
"reflect"
"sort"
"strconv"
"sync"
)
// OrderedMap is a map that maintains the order of keys.
type OrderedMap[K comparable, V any] struct {
mu sync.RWMutex
data map[K]V
order *list.List
index map[K]*list.Element
}
// NewOrderedMap creates a new OrderedMap.
func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V] {
return &OrderedMap[K, V]{
data: make(map[K]V),
order: list.New(),
index: make(map[K]*list.Element),
}
}
// Get returns the value for the given key.
// Play: todo
func (om *OrderedMap[K, V]) Set(key K, value V) {
om.mu.Lock()
defer om.mu.Unlock()
if elem, ok := om.index[key]; ok {
elem.Value = value
om.order.MoveToBack(elem)
return
}
om.data[key] = value
elem := om.order.PushBack(key)
om.index[key] = elem
}
// Get returns the value for the given key.
// Play: todo
func (om *OrderedMap[K, V]) Get(key K) (V, bool) {
om.mu.RLock()
defer om.mu.RUnlock()
value, ok := om.data[key]
return value, ok
}
// Delete deletes the given key.
// Play: todo
func (om *OrderedMap[K, V]) Delete(key K) {
om.mu.Lock()
defer om.mu.Unlock()
if elem, ok := om.index[key]; ok {
om.order.Remove(elem)
delete(om.data, key)
delete(om.index, key)
}
}
// Clear clears the map.
// Play: todo
func (om *OrderedMap[K, V]) Clear() {
om.mu.Lock()
defer om.mu.Unlock()
om.data = make(map[K]V)
om.order.Init()
om.index = make(map[K]*list.Element)
}
// Front returns the first key-value pair.
// Play: todo
func (om *OrderedMap[K, V]) Front() (struct {
Key K
Value V
}, bool) {
om.mu.RLock()
defer om.mu.RUnlock()
if elem := om.order.Front(); elem != nil {
key := elem.Value.(K)
value := om.data[key]
return struct {
Key K
Value V
}{
Key: key,
Value: value,
}, true
}
return struct {
Key K
Value V
}{}, false
}
// Back returns the last key-value pair.
// Play: todo
func (om *OrderedMap[K, V]) Back() (struct {
Key K
Value V
}, bool) {
om.mu.RLock()
defer om.mu.RUnlock()
if elem := om.order.Back(); elem != nil {
key := elem.Value.(K)
value := om.data[key]
return struct {
Key K
Value V
}{
Key: key,
Value: value,
}, true
}
return struct {
Key K
Value V
}{}, false
}
// Range calls the given function for each key-value pair.
// Play: todo
func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool) {
om.mu.RLock()
defer om.mu.RUnlock()
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
value := om.data[key]
if !iteratee(key, value) {
break
}
}
}
// Keys returns the keys in order.
// Play: todo
func (om *OrderedMap[K, V]) Keys() []K {
om.mu.RLock()
defer om.mu.RUnlock()
keys := make([]K, 0, len(om.data))
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
keys = append(keys, elem.Value.(K))
}
return keys
}
// Values returns the values in order.
// Play: todo
func (om *OrderedMap[K, V]) Values() []V {
om.mu.RLock()
defer om.mu.RUnlock()
values := make([]V, 0, len(om.data))
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
values = append(values, om.data[key])
}
return values
}
// Len returns the number of key-value pairs.
// Play: todo
func (om *OrderedMap[K, V]) Len() int {
om.mu.RLock()
defer om.mu.RUnlock()
return len(om.data)
}
// Contains returns true if the given key exists.
// Play: todo
func (om *OrderedMap[K, V]) Contains(key K) bool {
om.mu.RLock()
defer om.mu.RUnlock()
_, ok := om.data[key]
return ok
}
// Elements returns the key-value pairs in order.
// Play: todo
func (om *OrderedMap[K, V]) Elements() []struct {
Key K
Value V
} {
om.mu.RLock()
defer om.mu.RUnlock()
elements := make([]struct {
Key K
Value V
}, 0, len(om.data))
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
value := om.data[key]
elements = append(elements, struct {
Key K
Value V
}{Key: key, Value: value})
}
return elements
}
// Iter returns a channel that yields key-value pairs in order.
// Play: todo
func (om *OrderedMap[K, V]) Iter() <-chan struct {
Key K
Value V
} {
ch := make(chan struct {
Key K
Value V
})
go func() {
om.mu.RLock()
defer om.mu.RUnlock()
defer close(ch)
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
value := om.data[key]
ch <- struct {
Key K
Value V
}{Key: key, Value: value}
}
}()
return ch
}
// ReverseIter returns a channel that yields key-value pairs in reverse order.
// Play: todo
func (om *OrderedMap[K, V]) ReverseIter() <-chan struct {
Key K
Value V
} {
ch := make(chan struct {
Key K
Value V
})
go func() {
om.mu.RLock()
defer om.mu.RUnlock()
defer close(ch)
for elem := om.order.Back(); elem != nil; elem = elem.Prev() {
key := elem.Value.(K)
value := om.data[key]
ch <- struct {
Key K
Value V
}{Key: key, Value: value}
}
}()
return ch
}
// SortByValue sorts the map by key given less function.
// Play: todo
func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool) {
om.mu.Lock()
defer om.mu.Unlock()
keys := make([]K, 0, om.order.Len())
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
keys = append(keys, elem.Value.(K))
}
sort.Slice(keys, func(i, j int) bool {
return less(keys[i], keys[j])
})
om.order.Init()
om.index = make(map[K]*list.Element)
for _, key := range keys {
elem := om.order.PushBack(key)
om.index[key] = elem
}
}
// MarshalJSON implements the json.Marshaler interface.
// Play: todo
func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) {
om.mu.RLock()
defer om.mu.RUnlock()
tempMap := make(map[string]V)
for e := om.order.Front(); e != nil; e = e.Next() {
key := e.Value.(K)
keyStr, err := keyToString(key)
if err != nil {
return nil, err
}
tempMap[keyStr] = om.data[key]
}
return json.Marshal(tempMap)
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// Play: todo
func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error {
om.mu.Lock()
defer om.mu.Unlock()
tempMap := make(map[string]V)
if err := json.Unmarshal(data, &tempMap); err != nil {
return err
}
om.data = make(map[K]V)
om.order.Init()
om.index = make(map[K]*list.Element)
for keyStr, value := range tempMap {
key, err := stringToKey[K](keyStr)
if err != nil {
return err
}
om.data[key] = value
elem := om.order.PushBack(key)
om.index[key] = elem
}
return nil
}
func keyToString[K any](key K) (string, error) {
switch v := any(key).(type) {
case int:
return strconv.Itoa(v), nil
case float64:
return strconv.FormatFloat(v, 'f', -1, 64), nil
case string:
return v, nil
default:
// 使用反射将未知类型转换为字符串
rv := reflect.ValueOf(key)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10), nil
case reflect.Float32, reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'f', -1, 64), nil
case reflect.String:
return rv.String(), nil
default:
return "", fmt.Errorf("unsupported key type: %T", key)
}
}
}
func stringToKey[K any](s string) (K, error) {
var zero K
switch any(zero).(type) {
case int:
value, err := strconv.Atoi(s)
return any(value).(K), err
case float64:
value, err := strconv.ParseFloat(s, 64)
return any(value).(K), err
case string:
return any(s).(K), nil
default:
// 使用反射恢复未知类型的键
rv := reflect.ValueOf(&zero).Elem()
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return zero, err
}
rv.SetInt(val)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return zero, err
}
rv.SetUint(val)
case reflect.Float32, reflect.Float64:
val, err := strconv.ParseFloat(s, 64)
if err != nil {
return zero, err
}
rv.SetFloat(val)
case reflect.String:
rv.SetString(s)
default:
return zero, fmt.Errorf("unsupported key type: %T", zero)
}
return rv.Interface().(K), nil
}
}
+245
View File
@@ -0,0 +1,245 @@
package maputil
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestOrderedMap_Set_Get(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Set_Get")
val, ok := om.Get("a")
assert.Equal(1, val)
assert.Equal(true, ok)
val, ok = om.Get("d")
assert.Equal(false, ok)
assert.Equal(0, val)
}
func TestOrderedMap_Front_Back(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Front_Back")
frontElement, ok := om.Front()
assert.Equal("a", frontElement.Key)
assert.Equal(1, frontElement.Value)
assert.Equal(true, ok)
backElement, ok := om.Back()
assert.Equal("c", backElement.Key)
assert.Equal(3, backElement.Value)
assert.Equal(true, ok)
}
func TestOrderedMap_Delete_Clear(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Delete_Clear")
assert.Equal(3, om.Len())
om.Delete("b")
assert.Equal(2, om.Len())
om.Clear()
assert.Equal(0, om.Len())
}
func TestOrderedMap_Keys_Values(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Keys_Values")
assert.Equal([]string{"a", "b", "c"}, om.Keys())
assert.Equal([]int{1, 2, 3}, om.Values())
}
func TestOrderedMap_Contains(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Contains")
assert.Equal(true, om.Contains("a"))
assert.Equal(false, om.Contains("d"))
}
func TestOrderedMap_Eelements(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Eelements")
elements := []struct {
Key string
Value int
}{
{"a", 1},
{"b", 2},
{"c", 3},
}
assert.Equal(elements, om.Elements())
}
func TestOrderedMap_Range(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Set("d", 4)
assert := internal.NewAssert(t, "TestOrderedMap_Range")
var keys []string
om.Range(func(key string, value int) bool {
keys = append(keys, key)
return key != "c"
})
assert.Equal([]string{"a", "b", "c"}, keys)
}
func TestOrderedMap_Iter(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Iter")
var items []struct {
Key string
Value int
}
iterCh := om.Iter()
for item := range iterCh {
items = append(items, item)
}
expected := []struct {
Key string
Value int
}{
{"a", 1},
{"b", 2},
{"c", 3},
}
assert.Equal(expected, items)
}
func TestOrderedMap_ReverseIter(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_ReverseIter")
var items []struct {
Key string
Value int
}
iterCh := om.ReverseIter()
for item := range iterCh {
items = append(items, item)
}
expected := []struct {
Key string
Value int
}{
{"c", 3},
{"b", 2},
{"a", 1},
}
assert.Equal(expected, items)
}
func TestOrderedMap_SortByKey(t *testing.T) {
assert := internal.NewAssert(t, "TestOrderedMap_SortByKey")
om := NewOrderedMap[string, int]()
om.Set("d", 4)
om.Set("b", 2)
om.Set("c", 3)
om.Set("a", 1)
om.SortByKey(func(a, b string) bool {
return a < b
})
assert.Equal([]string{"a", "b", "c", "d"}, om.Keys())
}
func TestOrderedMap_MarshalJSON(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_MarshalJSON")
jsonBytes, err := om.MarshalJSON()
if err != nil {
t.Errorf("MarshalJSON error: %v", err)
}
assert.Equal(`{"a":1,"b":2,"c":3}`, string(jsonBytes))
}
func TestOrderedMap_UnmarshalJSON(t *testing.T) {
assert := internal.NewAssert(t, "TestOrderedMap_UnmarshalJSON")
jsonStr := `{"a":1,"b":2,"c":3}`
om := NewOrderedMap[string, int]()
err := om.UnmarshalJSON([]byte(jsonStr))
if err != nil {
t.Errorf("MarshalJSON error: %v", err)
}
assert.Equal(3, om.Len())
assert.Equal(true, om.Contains("a"))
assert.Equal(true, om.Contains("b"))
assert.Equal(true, om.Contains("c"))
}
+232 -138
View File
@@ -16,12 +16,20 @@ func TestContain(t *testing.T) {
assert := internal.NewAssert(t, "TestContain") assert := internal.NewAssert(t, "TestContain")
assert.Equal(true, Contain([]string{"a", "b", "c"}, "a")) tests := []struct {
assert.Equal(false, Contain([]string{"a", "b", "c"}, "d")) slice []string
assert.Equal(true, Contain([]string{""}, "")) give string
assert.Equal(false, Contain([]string{}, "")) want bool
}{
{[]string{"a", "b", "c"}, "a", true},
{[]string{"a", "b", "c"}, "d", false},
{[]string{""}, "", true},
{[]string{}, "", false},
}
assert.Equal(true, Contain([]int{1, 2, 3}, 1)) for _, tt := range tests {
assert.Equal(tt.want, Contain(tt.slice, tt.give))
}
} }
func TestContainBy(t *testing.T) { func TestContainBy(t *testing.T) {
@@ -30,32 +38,53 @@ func TestContainBy(t *testing.T) {
assert := internal.NewAssert(t, "TestContainBy") assert := internal.NewAssert(t, "TestContainBy")
type foo struct { type foo struct {
A string a string
B int b int
} }
array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}} tests := []struct {
result1 := ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 }) slice []foo
result2 := ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 }) predicateFn func(f foo) bool
want bool
}{
{
[]foo{{a: "1", b: 1}, {a: "2", b: 2}},
func(f foo) bool { return f.a == "1" && f.b == 1 },
true,
},
{
[]foo{{a: "1", b: 1}, {a: "2", b: 2}},
func(f foo) bool { return f.a == "2" && f.b == 1 },
false,
},
}
array2 := []string{"a", "b", "c"} for _, tt := range tests {
result3 := ContainBy(array2, func(t string) bool { return t == "a" }) assert.Equal(tt.want, ContainBy(tt.slice, tt.predicateFn))
result4 := ContainBy(array2, func(t string) bool { return t == "d" }) }
assert.Equal(true, result1)
assert.Equal(false, result2)
assert.Equal(true, result3)
assert.Equal(false, result4)
} }
func TestContainSubSlice(t *testing.T) { func TestContainSubSlice(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestContainSubSlice") assert := internal.NewAssert(t, "TestContainSubSlice")
assert.Equal(true, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "a"}))
assert.Equal(false, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "d"})) tests := []struct {
assert.Equal(true, ContainSubSlice([]int{1, 2, 3}, []int{1, 2})) slice []string
assert.Equal(false, ContainSubSlice([]int{1, 2, 3}, []int{0, 1})) subSlice []string
want bool
}{
{[]string{"a", "b", "c"}, []string{"a", "b"}, true},
{[]string{"a", "b", "c"}, []string{"a", "d"}, false},
{[]string{"a", "b", "c"}, []string{"a", "b", "c"}, true},
{[]string{"a", "b", "c"}, []string{"a", "b", "c", "d"}, false},
{[]string{"a", "b", ""}, []string{"a", ""}, true},
{[]string{""}, []string{""}, true},
}
for _, tt := range tests {
assert.Equal(tt.want, ContainSubSlice(tt.slice, tt.subSlice))
}
} }
func TestChunk(t *testing.T) { func TestChunk(t *testing.T) {
@@ -63,29 +92,24 @@ func TestChunk(t *testing.T) {
assert := internal.NewAssert(t, "TestChunk") assert := internal.NewAssert(t, "TestChunk")
arr := []string{"a", "b", "c", "d", "e"} tests := []struct {
slice []string
chuanSize int
want [][]string
}{
{[]string{"a", "b", "c", "d", "e"}, -1, [][]string{}},
{[]string{"a", "b", "c", "d", "e"}, 0, [][]string{}},
{[]string{"a", "b", "c", "d", "e"}, 1, [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}},
{[]string{"a", "b", "c", "d", "e"}, 2, [][]string{{"a", "b"}, {"c", "d"}, {"e"}}},
{[]string{"a", "b", "c", "d", "e"}, 3, [][]string{{"a", "b", "c"}, {"d", "e"}}},
{[]string{"a", "b", "c", "d", "e"}, 4, [][]string{{"a", "b", "c", "d"}, {"e"}}},
{[]string{"a", "b", "c", "d", "e"}, 5, [][]string{{"a", "b", "c", "d", "e"}}},
{[]string{"a", "b", "c", "d", "e"}, 6, [][]string{{"a", "b", "c", "d", "e"}}},
}
assert.Equal([][]string{}, Chunk(arr, -1)) for _, tt := range tests {
assert.Equal(tt.want, Chunk(tt.slice, tt.chuanSize))
assert.Equal([][]string{}, Chunk(arr, 0)) }
r1 := [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
assert.Equal(r1, Chunk(arr, 1))
r2 := [][]string{{"a", "b"}, {"c", "d"}, {"e"}}
assert.Equal(r2, Chunk(arr, 2))
r3 := [][]string{{"a", "b", "c"}, {"d", "e"}}
assert.Equal(r3, Chunk(arr, 3))
r4 := [][]string{{"a", "b", "c", "d"}, {"e"}}
assert.Equal(r4, Chunk(arr, 4))
r5 := [][]string{{"a", "b", "c", "d", "e"}}
assert.Equal(r5, Chunk(arr, 5))
r6 := [][]string{{"a", "b", "c", "d", "e"}}
assert.Equal(r6, Chunk(arr, 6))
} }
func TestCompact(t *testing.T) { func TestCompact(t *testing.T) {
@@ -134,6 +158,7 @@ func TestEqual(t *testing.T) {
assert.Equal(true, Equal(slice1, slice2)) assert.Equal(true, Equal(slice1, slice2))
assert.Equal(false, Equal(slice1, slice3)) assert.Equal(false, Equal(slice1, slice3))
assert.Equal(false, Equal(slice2, slice3))
} }
// go test -fuzz=Fuzz -fuzztime=10s . // go test -fuzz=Fuzz -fuzztime=10s .
@@ -163,12 +188,27 @@ func TestEvery(t *testing.T) {
assert := internal.NewAssert(t, "TestEvery") assert := internal.NewAssert(t, "TestEvery")
nums := []int{1, 2, 3, 5}
isEven := func(i, num int) bool { isEven := func(i, num int) bool {
return num%2 == 0 return num%2 == 0
} }
isOdd := func(i, num int) bool {
return num%2 == 1
}
assert.Equal(false, Every(nums, isEven)) tests := []struct {
slice []int
predicateFn func(i, num int) bool
want bool
}{
{[]int{1, 3, 5, 7}, isOdd, true},
{[]int{2, 4, 6, 8}, isEven, true},
{[]int{1, 2, 3, 4}, isOdd, false},
{[]int{1, 2, 3, 4}, isEven, false},
}
for _, tt := range tests {
assert.Equal(tt.want, Every(tt.slice, tt.predicateFn))
}
} }
func TestNone(t *testing.T) { func TestNone(t *testing.T) {
@@ -176,12 +216,27 @@ func TestNone(t *testing.T) {
assert := internal.NewAssert(t, "TestNone") assert := internal.NewAssert(t, "TestNone")
nums := []int{1, 2, 3, 5} isEven := func(i, num int) bool {
check := func(i, num int) bool { return num%2 == 0
}
isOdd := func(i, num int) bool {
return num%2 == 1 return num%2 == 1
} }
assert.Equal(false, None(nums, check)) tests := []struct {
slice []int
predicateFn func(i, num int) bool
want bool
}{
{[]int{1, 3, 5, 7}, isEven, true},
{[]int{2, 4, 6, 8}, isOdd, true},
{[]int{1, 2, 3, 4}, isOdd, false},
{[]int{1, 2, 3, 4}, isEven, false},
}
for _, tt := range tests {
assert.Equal(tt.want, None(tt.slice, tt.predicateFn))
}
} }
func TestSome(t *testing.T) { func TestSome(t *testing.T) {
@@ -189,12 +244,27 @@ func TestSome(t *testing.T) {
assert := internal.NewAssert(t, "TestSome") assert := internal.NewAssert(t, "TestSome")
nums := []int{1, 2, 3, 5} isEven := func(i, num int) bool {
hasEven := func(i, num int) bool {
return num%2 == 0 return num%2 == 0
} }
isOdd := func(i, num int) bool {
return num%2 == 1
}
assert.Equal(true, Some(nums, hasEven)) tests := []struct {
slice []int
predicateFn func(i, num int) bool
want bool
}{
{[]int{1, 3, 5, 7}, isEven, false},
{[]int{2, 4, 6, 8}, isOdd, false},
{[]int{1, 2, 3, 4}, isOdd, true},
{[]int{1, 2, 3, 4}, isEven, true},
}
for _, tt := range tests {
assert.Equal(tt.want, Some(tt.slice, tt.predicateFn))
}
} }
func TestFilter(t *testing.T) { func TestFilter(t *testing.T) {
@@ -202,13 +272,16 @@ func TestFilter(t *testing.T) {
assert := internal.NewAssert(t, "TestFilter") assert := internal.NewAssert(t, "TestFilter")
t.Run("filter int slice", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5} nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool { isEven := func(i, num int) bool {
return num%2 == 0 return num%2 == 0
} }
assert.Equal([]int{2, 4}, Filter(nums, isEven)) assert.Equal([]int{2, 4}, Filter(nums, isEven))
})
t.Run("filter struct slice", func(t *testing.T) {
type student struct { type student struct {
name string name string
age int age int
@@ -220,7 +293,7 @@ func TestFilter(t *testing.T) {
{"d", 13}, {"d", 13},
{"e", 14}, {"e", 14},
} }
studentsOfAageGreat12 := []student{ studentsOfAgeGreat12 := []student{
{"d", 13}, {"d", 13},
{"e", 14}, {"e", 14},
} }
@@ -228,7 +301,8 @@ func TestFilter(t *testing.T) {
return s.age > 12 return s.age > 12
} }
assert.Equal(studentsOfAageGreat12, Filter(students, filterFunc)) assert.Equal(studentsOfAgeGreat12, Filter(students, filterFunc))
})
} }
func TestGroupBy(t *testing.T) { func TestGroupBy(t *testing.T) {
@@ -249,6 +323,8 @@ func TestGroupBy(t *testing.T) {
func TestGroupWith(t *testing.T) { func TestGroupWith(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestGroupWith")
nums := []float64{6.1, 4.2, 6.3} nums := []float64{6.1, 4.2, 6.3}
floor := func(num float64) float64 { floor := func(num float64) float64 {
return math.Floor(num) return math.Floor(num)
@@ -257,9 +333,8 @@ func TestGroupWith(t *testing.T) {
4: {4.2}, 4: {4.2},
6: {6.1, 6.3}, 6: {6.1, 6.3},
} }
actual := GroupWith(nums, floor)
assert := internal.NewAssert(t, "TestGroupWith") assert.Equal(expected, GroupWith(nums, floor))
assert.Equal(expected, actual)
} }
func TestCount(t *testing.T) { func TestCount(t *testing.T) {
@@ -295,8 +370,15 @@ func TestFind(t *testing.T) {
result, ok := Find(nums, even) result, ok := Find(nums, even)
assert := internal.NewAssert(t, "TestFind") assert := internal.NewAssert(t, "TestFind")
assert.Equal(true, ok) assert.Equal(true, ok)
assert.Equal(2, *result) assert.Equal(2, *result)
_, ok = Find(nums, func(_ int, v int) bool {
return v == 6
})
assert.Equal(false, ok)
} }
func TestFindBy(t *testing.T) { func TestFindBy(t *testing.T) {
@@ -358,19 +440,6 @@ func TestFindLast(t *testing.T) {
assert.Equal(4, *result) assert.Equal(4, *result)
} }
func TestFindFoundNothing(t *testing.T) {
t.Parallel()
nums := []int{1, 1, 1, 1, 1, 1}
findFunc := func(i, num int) bool {
return num > 1
}
_, ok := Find(nums, findFunc)
assert := internal.NewAssert(t, "TestFindFoundNothing")
assert.Equal(false, ok)
}
func TestFlatten(t *testing.T) { func TestFlatten(t *testing.T) {
t.Parallel() t.Parallel()
@@ -633,14 +702,12 @@ func TestReduceBy(t *testing.T) {
result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int { result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int {
return agg + item return agg + item
}) })
assert.Equal(10, result1)
result2 := ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string { result2 := ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string {
return agg + fmt.Sprintf("%v", item) return agg + fmt.Sprintf("%v", item)
}) })
assert.Equal(10, result1)
assert.Equal("1234", result2) assert.Equal("1234", result2)
} }
func TestReduceRight(t *testing.T) { func TestReduceRight(t *testing.T) {
@@ -692,15 +759,23 @@ func TestDeleteAt(t *testing.T) {
t.Parallel() t.Parallel()
assert := internal.NewAssert(t, "TestDeleteAt") assert := internal.NewAssert(t, "TestDeleteAt")
arr := []int{1, 2, 3, 4, 5}
assert.Equal([]int{2, 3, 4, 5}, DeleteAt(arr, 0)) tests := []struct {
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 4)) slice []int
deletePos int
wang []int
}{
{[]int{1, 2, 3, 4, 5}, 0, []int{2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 1, []int{1, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 2, []int{1, 2, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 3, []int{1, 2, 3, 5}},
{[]int{1, 2, 3, 4, 5}, 4, []int{1, 2, 3, 4}},
{[]int{1, 2, 3, 4, 5}, 5, []int{1, 2, 3, 4}},
}
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 5)) for _, tt := range tests {
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 6)) assert.Equal(tt.wang, DeleteAt(tt.slice, tt.deletePos))
}
assert.Equal([]int{1, 2, 3, 4, 5}, arr)
} }
func TestDeleteRange(t *testing.T) { func TestDeleteRange(t *testing.T) {
@@ -720,16 +795,24 @@ func TestDrop(t *testing.T) {
assert := internal.NewAssert(t, "TestDrop") assert := internal.NewAssert(t, "TestDrop")
assert.Equal([]int{}, Drop([]int{}, 0)) tests := []struct {
assert.Equal([]int{}, Drop([]int{}, 1)) slice []int
assert.Equal([]int{}, Drop([]int{}, -1)) dropNum int
want []int
}{
{[]int{}, 0, []int{}},
{[]int{}, 1, []int{}},
{[]int{}, -1, []int{}},
{[]int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 0, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 1, []int{2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 5, []int{}},
{[]int{1, 2, 3, 4, 5}, 6, []int{}},
}
assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 0)) for _, tt := range tests {
assert.Equal([]int{2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 1)) assert.Equal(tt.want, Drop(tt.slice, tt.dropNum))
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 5)) }
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 6))
assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, -1))
} }
func TestDropRight(t *testing.T) { func TestDropRight(t *testing.T) {
@@ -737,16 +820,23 @@ func TestDropRight(t *testing.T) {
assert := internal.NewAssert(t, "TestDropRight") assert := internal.NewAssert(t, "TestDropRight")
assert.Equal([]int{}, DropRight([]int{}, 0)) tests := []struct {
assert.Equal([]int{}, DropRight([]int{}, 1)) slice []int
assert.Equal([]int{}, DropRight([]int{}, -1)) dropNum int
want []int
assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, 0)) }{
assert.Equal([]int{1, 2, 3, 4}, DropRight([]int{1, 2, 3, 4, 5}, 1)) {[]int{}, 0, []int{}},
assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 5)) {[]int{}, 1, []int{}},
assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 6)) {[]int{}, -1, []int{}},
{[]int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}},
assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, -1)) {[]int{1, 2, 3, 4, 5}, 0, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 1, []int{1, 2, 3, 4}},
{[]int{}, 5, []int{}},
{[]int{}, 6, []int{}},
}
for _, tt := range tests {
assert.Equal(tt.want, DropRight(tt.slice, tt.dropNum))
}
} }
func TestDropWhile(t *testing.T) { func TestDropWhile(t *testing.T) {
@@ -754,22 +844,19 @@ func TestDropWhile(t *testing.T) {
assert := internal.NewAssert(t, "TestDropWhile") assert := internal.NewAssert(t, "TestDropWhile")
numbers := []int{1, 2, 3, 4, 5} tests := []struct {
slice []int
fn func(int) bool
want []int
}{
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n != 2 }, []int{2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return true }, []int{}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 0 }, []int{1, 2, 3, 4, 5}},
}
r1 := DropWhile(numbers, func(n int) bool { for _, tt := range tests {
return n != 2 assert.Equal(tt.want, DropWhile(tt.slice, tt.fn))
}) }
assert.Equal([]int{2, 3, 4, 5}, r1)
r2 := DropWhile(numbers, func(n int) bool {
return true
})
assert.Equal([]int{}, r2)
r3 := DropWhile(numbers, func(n int) bool {
return n == 0
})
assert.Equal([]int{1, 2, 3, 4, 5}, r3)
} }
func TestDropRightWhile(t *testing.T) { func TestDropRightWhile(t *testing.T) {
@@ -777,22 +864,19 @@ func TestDropRightWhile(t *testing.T) {
assert := internal.NewAssert(t, "TestDropRightWhile") assert := internal.NewAssert(t, "TestDropRightWhile")
numbers := []int{1, 2, 3, 4, 5} tests := []struct {
slice []int
fn func(int) bool
want []int
}{
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n != 2 }, []int{1, 2}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return true }, []int{}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 0 }, []int{1, 2, 3, 4, 5}},
}
r1 := DropRightWhile(numbers, func(n int) bool { for _, tt := range tests {
return n != 2 assert.Equal(tt.want, DropRightWhile(tt.slice, tt.fn))
}) }
assert.Equal([]int{1, 2}, r1)
r2 := DropRightWhile(numbers, func(n int) bool {
return true
})
assert.Equal([]int{}, r2)
r3 := DropRightWhile(numbers, func(n int) bool {
return n == 0
})
assert.Equal([]int{1, 2, 3, 4, 5}, r3)
} }
func TestInsertAt(t *testing.T) { func TestInsertAt(t *testing.T) {
@@ -800,15 +884,25 @@ func TestInsertAt(t *testing.T) {
assert := internal.NewAssert(t, "TestInsertAt") assert := internal.NewAssert(t, "TestInsertAt")
strs := []string{"a", "b", "c"} tests := []struct {
assert.Equal([]string{"a", "b", "c"}, InsertAt(strs, -1, "1")) slice []string
assert.Equal([]string{"a", "b", "c"}, InsertAt(strs, 4, "1")) insertPos int
assert.Equal([]string{"1", "a", "b", "c"}, InsertAt(strs, 0, "1")) insertValue any
assert.Equal([]string{"a", "1", "b", "c"}, InsertAt(strs, 1, "1")) want []string
assert.Equal([]string{"a", "b", "1", "c"}, InsertAt(strs, 2, "1")) }{
assert.Equal([]string{"a", "b", "c", "1"}, InsertAt(strs, 3, "1")) {[]string{"a", "b", "c"}, -1, "1", []string{"a", "b", "c"}},
assert.Equal([]string{"1", "2", "3", "a", "b", "c"}, InsertAt(strs, 0, []string{"1", "2", "3"})) {[]string{"a", "b", "c"}, 4, "1", []string{"a", "b", "c"}},
assert.Equal([]string{"a", "b", "c", "1", "2", "3"}, InsertAt(strs, 3, []string{"1", "2", "3"})) {[]string{"a", "b", "c"}, 0, "1", []string{"1", "a", "b", "c"}},
{[]string{"a", "b", "c"}, 1, "1", []string{"a", "1", "b", "c"}},
{[]string{"a", "b", "c"}, 2, "1", []string{"a", "b", "1", "c"}},
{[]string{"a", "b", "c"}, 3, "1", []string{"a", "b", "c", "1"}},
{[]string{"a", "b", "c"}, 0, []string{"1", "2", "3"}, []string{"1", "2", "3", "a", "b", "c"}},
{[]string{"a", "b", "c"}, 3, []string{"1", "2", "3"}, []string{"a", "b", "c", "1", "2", "3"}},
}
for _, tt := range tests {
assert.Equal(tt.want, InsertAt(tt.slice, tt.insertPos, tt.insertValue))
}
} }
func TestUpdateAt(t *testing.T) { func TestUpdateAt(t *testing.T) {