diff --git a/README_zh-CN.md b/README_zh-CN.md index a261320..d8c27d3 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -1200,9 +1200,6 @@ import "github.com/duke-git/lancet/v2/retry" - **RetryFunc** : 重试执行的函数。 [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryFunc)] [[play](https://go.dev/play/p/nk2XRmagfVF)] -- **RetryDuration** : 设置重试间隔时间,默认 3 秒。 - [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryDuration)] - [[play](https://go.dev/play/p/nk2XRmagfVF)] - **RetryTimes** : 设置重试次数,默认 5。 [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryTimes)] [[play](https://go.dev/play/p/ssfVeU2SwLO)] diff --git a/docs/api/packages/maputil.md b/docs/api/packages/maputil.md index c8321bc..c85cb40 100644 --- a/docs/api/packages/maputil.md +++ b/docs/api/packages/maputil.md @@ -47,6 +47,24 @@ import ( - [MapToStruct](#MapToStruct) - [ToSortedSlicesDefault](#ToSortedSlicesDefault) - [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator) +- [NewOrderedMap](#NewOrderedMap) +- [OrderedMap_Set](#OrderedMap_Set) +- [OrderedMap_Get](#OrderedMap_Get) +- [OrderedMap_Front](#OrderedMap_Front) +- [OrderedMap_Back](#OrderedMap_Back) +- [OrderedMap_Delete](#OrderedMap_Delete) +- [OrderedMap_Clear](#OrderedMap_Clear) +- [OrderedMap_Len](#OrderedMap_Len) +- [OrderedMap_Keys](#OrderedMap_Keys) +- [OrderedMap_Values](#OrderedMap_Values) +- [OrderedMap_Contains](#OrderedMap_Contains) +- [OrderedMap_Range](#OrderedMap_Range) +- [OrderedMap_Elements](#OrderedMap_Elements) +- [OrderedMap_Iter](#OrderedMap_Iter) +- [OrderedMap_ReverseIter](#OrderedMap_ReverseIter) +- [OrderedMap_SortByKey](#OrderedMap_SortByKey) +- [OrderedMap_MarshalJSON](#OrderedMap_MarshalJSON) +- [OrderedMap_UnmarshalJSON](#OrderedMap_UnmarshalJSON) - [NewConcurrentMap](#NewConcurrentMap) - [ConcurrentMap_Get](#ConcurrentMap_Get) - [ConcurrentMap_Set](#ConcurrentMap_Set) @@ -56,6 +74,7 @@ import ( - [ConcurrentMap_Has](#ConcurrentMap_Has) - [ConcurrentMap_Range](#ConcurrentMap_Range) - [GetOrSet](#GetOrSet) +- [SortByKey](#SortByKey)
@@ -1120,6 +1139,689 @@ func main() { } ``` +### NewOrderedMap + +创建有序映射。有序映射是键值对的集合,其中键是唯一的,并且保留键插入的顺序。
+ +函数签名: + +```go +func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V] +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + +### OrderedMap_Set + +设置给定的键值对。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Set(key K, value V) +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + +### OrderedMap_Get + +返回给定键的值。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Get(key K) (V, bool) +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + + +### OrderedMap_Delete + +删除给定键的键值对。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Delete(key K) +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Delete("b") + + fmt.Println(om.Keys()) + + // Output: + // [a c] +} +``` + +### OrderedMap_Clear + +清空map数据。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Clear() +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Clear() + + fmt.Println(om.Keys()) + + // Output: + // [] +} +``` + +### OrderedMap_Front + +返回第一个键值对。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Front() (struct { + Key K + Value V +}, bool) +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + frontElement, ok := om.Front() + fmt.Println(frontElement) + fmt.Println(ok) + + // Output: + // {a 1} + // true +} +``` + +### OrderedMap_Back + +返回最后一个键值对。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Back() (struct { + Key K + Value V +}, bool) +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + frontElement, ok := om.Front() + fmt.Println(frontElement) + fmt.Println(ok) + + // Output: + // {a 1} + // true +} +``` + +### OrderedMap_Range + +为每个键值对调用给定的函数。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool) +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Range(func(key string, value int) bool { + fmt.Println(key, value) + return true + }) + + // Output: + // a 1 + // b 2 + // c 3 +} +``` + +### OrderedMap_Keys + +按顺序返回键的切片。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Keys() []K +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + keys := om.Keys() + + fmt.Println(keys) + + // Output: + // [a b c] +} +``` + +### OrderedMap_Values + +按顺序返回值的切片。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Values() []V +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + values := om.Values() + + fmt.Println(values) + + // Output: + // [1 2 3] +} +``` + +### OrderedMap_Elements + +按顺序返回键值对。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Elements() []struct +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + elements := om.Elements() + + fmt.Println(elements) + + // Output: + // [{a 1} {b 2} {c 3}] +} +``` + +### OrderedMap_Len + +返回键值对的数量。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Len() int +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Len() + + fmt.Println(om.Len()) + + // Output: + // 3 +} +``` + +### OrderedMap_Contains + +如果给定的键存在则返回true。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Contains(key K) bool +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + result1 := om.Contains("a") + result2 := om.Contains("d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### OrderedMap_Iter + +返回按顺序产生键值对的通道。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) Iter() <-chan struct { + Key K + Value V +} +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.Iter() { + fmt.Println(elem) + } + + // Output: + // {a 1} + // {b 2} + // {c 3} +} +``` + +### OrderedMap_ReverseIter + +返回以相反顺序产生键值对的通道。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) ReverseIter() <-chan struct { + Key K + Value V +} +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.ReverseIter() { + fmt.Println(elem) + } + + // Output: + // {c 3} + // {b 2} + // {a 1} +} +``` + +### OrderedMap_SortByKey + +使用传入的比较函数排序map key。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool) +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set(3, "c") + om.Set(1, "a") + om.Set(4, "d") + om.Set(2, "b") + + om.SortByKey(func(a, b int) bool { + return a < b + }) + + fmt.Println(om.Elements()) + + // Output: + // [{1 a} {2 b} {3 c} {4 d}] +} +``` + +### OrderedMap_MarshalJSON + +实现json.Marshaler接口。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set(3, "c") + om.Set(1, "a") + om.Set(4, "d") + om.Set(2, "b") + + b, _ := om.MarshalJSON() + + fmt.Println(string(b)) + + // Output: + // {"a":1,"b":2,"c":3} +} +``` + +### OrderedMap_UnmarshalJSON + +实现json.Unmarshaler接口。
+ +函数签名: + +```go +func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error +``` + +示例:[运行]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + data := []byte(`{"a":1,"b":2,"c":3}`) + + om.UnmarshalJSON(data) + + fmt.Println(om.Elements()) + + // Output: +} +``` + ### NewConcurrentMapConcurrentMap协程安全的map结构。
@@ -1522,4 +2224,41 @@ func main() { // a // b } +``` + +### SortByKey + +对传入的map根据key进行排序,返回排序后的map。
+ +函数签名: + +```go +func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V) +``` + +示例:[运行]() + +```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] +} ``` \ No newline at end of file diff --git a/docs/api/packages/retry.md b/docs/api/packages/retry.md index 1c065c2..165bd73 100644 --- a/docs/api/packages/retry.md +++ b/docs/api/packages/retry.md @@ -70,7 +70,7 @@ func main() { return errors.New("error occurs") } - duration := retry.RetryDuration(time.Microsecond*50) + duration := retry.RetryWithLinearBackoff(time.Microsecond*50) retry.Retry(increaseNumber, duration, @@ -116,7 +116,7 @@ func main() { return errors.New("error occurs") } - duration := retry.RetryDuration(time.Microsecond*50) + duration := retry.RetryWithLinearBackoff(time.Microsecond*50) err := retry.Retry(increaseNumber, duration) if err != nil { @@ -173,52 +173,6 @@ func main() { } ``` -### RetryDuration - -设置重试间隔时间,默认3秒
- -函数签名: - -```go -func RetryDuration(d time.Duration) -``` - -示例:[运行](https://go.dev/play/p/nk2XRmagfVF) - -```go -package main - -import ( - "fmt" - "errors" - "log" - "github.com/duke-git/lancet/v2/retry" -) - -func main() { - number := 0 - increaseNumber := func() error { - number++ - if number == 3 { - return nil - } - return errors.New("error occurs") - } - - duration := retry.RetryDuration(time.Microsecond*50) - - err := retry.Retry(increaseNumber, duration) - if err != nil { - return - } - - fmt.Println(number) - - // Output: - // 3 -} -``` - ### Retry重试执行函数retryFunc,直到函数运行成功,或被context停止
@@ -251,7 +205,7 @@ func main() { return errors.New("error occurs") } - duration := retry.RetryDuration(time.Microsecond*50) + duration := retry.RetryWithLinearBackoff(time.Microsecond*50) err := retry.Retry(increaseNumber, duration) if err != nil { diff --git a/docs/api/packages/system.md b/docs/api/packages/system.md index 3d5d360..e9b673b 100644 --- a/docs/api/packages/system.md +++ b/docs/api/packages/system.md @@ -241,7 +241,8 @@ func main() { ### ExecCommand -执行shell命令,返回命令的stdout和stderr字符串,如果出现错误,则返回错误。参数`command`是一个完整的命令字符串,如ls-a(linux),dir(windows),ping 127.0.0.1。在linux中,使用/bin/bash-c执行命令,在windows中,使用powershell.exe执行命令。
+执行shell命令,返回命令的stdout和stderr字符串,如果出现错误,则返回错误。参数`command`是一个完整的命令字符串,如ls-a(linux),dir(windows),ping 127.0.0.1。在linux中,使用/bin/bash-c执行命令,在windows中,使用powershell.exe执行命令。 +函数的第二个参数是cmd选项控制参数,类型是func(*exec.Cmd),可以通过这个参数设置cmd属性。
函数签名: @@ -262,7 +263,9 @@ import ( func main() { // linux or mac - stdout, stderr, err := system.ExecCommand("ls") + stdout, stderr, err := system.ExecCommand("ls", func(cmd *exec.Cmd) { + cmd.Dir = "/tmp" + }) fmt.Println("std out: ", stdout) fmt.Println("std err: ", stderr) assert.Equal("", stderr) diff --git a/docs/en/api/packages/maputil.md b/docs/en/api/packages/maputil.md index d3a3c52..243e55a 100644 --- a/docs/en/api/packages/maputil.md +++ b/docs/en/api/packages/maputil.md @@ -47,6 +47,24 @@ import ( - [MapToStruct](#MapToStruct) - [ToSortedSlicesDefault](#ToSortedSlicesDefault) - [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator) +- [NewOrderedMap](#NewOrderedMap) +- [OrderedMap_Set](#OrderedMap_Set) +- [OrderedMap_Get](#OrderedMap_Get) +- [OrderedMap_Front](#OrderedMap_Front) +- [OrderedMap_Back](#OrderedMap_Back) +- [OrderedMap_Delete](#OrderedMap_Delete) +- [OrderedMap_Clear](#OrderedMap_Clear) +- [OrderedMap_Len](#OrderedMap_Len) +- [OrderedMap_Keys](#OrderedMap_Keys) +- [OrderedMap_Values](#OrderedMap_Values) +- [OrderedMap_Contains](#OrderedMap_Contains) +- [OrderedMap_Range](#OrderedMap_Range) +- [OrderedMap_Elements](#OrderedMap_Elements) +- [OrderedMap_Iter](#OrderedMap_Iter) +- [OrderedMap_ReverseIter](#OrderedMap_ReverseIter) +- [OrderedMap_SortByKey](#OrderedMap_SortByKey) +- [OrderedMap_MarshalJSON](#OrderedMap_MarshalJSON) +- [OrderedMap_UnmarshalJSON](#OrderedMap_UnmarshalJSON) - [NewConcurrentMap](#NewConcurrentMap) - [ConcurrentMap_Get](#ConcurrentMap_Get) - [ConcurrentMap_Set](#ConcurrentMap_Set) @@ -1127,10 +1145,693 @@ func main() { fmt.Println(values1) // Output: - // [3 2 1] - // [c b a] - // [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC] - // [yesterday today tomorrow] + // [3 2 1] + // [c b a] + // [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC] + // [yesterday today tomorrow] +} +``` + +### NewOrderedMap + +Creates a new OrderedMap.
+ +Signature: + +```go +func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V] +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + +### OrderedMap_Set + +Sets the given key-value pair.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Set(key K, value V) +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + +### OrderedMap_Get + +Returns the value for the given key.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Get(key K) (V, bool) +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} +``` + + +### OrderedMap_Delete + +Deletes the key-value pair for the given key.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Delete(key K) +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Delete("b") + + fmt.Println(om.Keys()) + + // Output: + // [a c] +} +``` + +### OrderedMap_Clear + +Clears the map.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Clear() +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Clear() + + fmt.Println(om.Keys()) + + // Output: + // [] +} +``` + +### OrderedMap_Front + +Returns the first key-value pair.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Front() (struct { + Key K + Value V +}, bool) +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + frontElement, ok := om.Front() + fmt.Println(frontElement) + fmt.Println(ok) + + // Output: + // {a 1} + // true +} +``` + +### OrderedMap_Back + +Returns the last key-value pair.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Back() (struct { + Key K + Value V +}, bool) +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + frontElement, ok := om.Front() + fmt.Println(frontElement) + fmt.Println(ok) + + // Output: + // {a 1} + // true +} +``` + +### OrderedMap_Range + +Calls the given function for each key-value pair.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool) +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Range(func(key string, value int) bool { + fmt.Println(key, value) + return true + }) + + // Output: + // a 1 + // b 2 + // c 3 +} +``` + +### OrderedMap_Keys + +Returns the keys in order.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Keys() []K +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + keys := om.Keys() + + fmt.Println(keys) + + // Output: + // [a b c] +} +``` + +### OrderedMap_Values + +Returns the values in order.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Values() []V +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + values := om.Values() + + fmt.Println(values) + + // Output: + // [1 2 3] +} +``` + +### OrderedMap_Elements + +Returns the key-value pairs in order.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Elements() []struct +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + elements := om.Elements() + + fmt.Println(elements) + + // Output: + // [{a 1} {b 2} {c 3}] +} +``` + +### OrderedMap_Len + +Returns the number of key-value pairs.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Len() int +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Len() + + fmt.Println(om.Len()) + + // Output: + // 3 +} +``` + +### OrderedMap_Contains + +Returns true if the given key exists.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Contains(key K) bool +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + result1 := om.Contains("a") + result2 := om.Contains("d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} +``` + +### OrderedMap_Iter + +Returns a channel that yields key-value pairs in order.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) Iter() <-chan struct { + Key K + Value V +} +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.Iter() { + fmt.Println(elem) + } + + // Output: + // {a 1} + // {b 2} + // {c 3} +} +``` + +### OrderedMap_ReverseIter + +Returns a channel that yields key-value pairs in reverse order.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) ReverseIter() <-chan struct { + Key K + Value V +} +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.ReverseIter() { + fmt.Println(elem) + } + + // Output: + // {c 3} + // {b 2} + // {a 1} +} +``` + +### OrderedMap_SortByKey + +Sorts the map by key given less function.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool) +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set(3, "c") + om.Set(1, "a") + om.Set(4, "d") + om.Set(2, "b") + + om.SortByKey(func(a, b int) bool { + return a < b + }) + + fmt.Println(om.Elements()) + + // Output: + // [{1 a} {2 b} {3 c} {4 d}] +} +``` + +### OrderedMap_MarshalJSON + +Implements the json.Marshaler interface.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + om.Set(3, "c") + om.Set(1, "a") + om.Set(4, "d") + om.Set(2, "b") + + b, _ := om.MarshalJSON() + + fmt.Println(string(b)) + + // Output: + // {"a":1,"b":2,"c":3} +} +``` + +### OrderedMap_UnmarshalJSON + +Implements the json.Unmarshaler interface.
+ +Signature: + +```go +func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error +``` + +Example:[Run]() + +```go +package main + +import ( + "fmt" + "github.com/duke-git/lancet/v2/maputil" +) + +func main() { + om := maputil.NewOrderedMap[string, int]() + + data := []byte(`{"a":1,"b":2,"c":3}`) + + om.UnmarshalJSON(data) + + fmt.Println(om.Elements()) + + // Output: } ``` @@ -1538,4 +2239,41 @@ func main() { // a // b } +``` + +### SortByKey + +Sorts the map by its keys and returns a new map with sorted keys.
+ +Signature: + +```go +func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V) +``` + +Example:[运行]() + +```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] +} ``` \ No newline at end of file diff --git a/docs/en/api/packages/system.md b/docs/en/api/packages/system.md index 5f0747e..1fd130e 100644 --- a/docs/en/api/packages/system.md +++ b/docs/en/api/packages/system.md @@ -242,7 +242,8 @@ func main() { ### ExecCommand -Execute shell command, return the stdout and stderr string of command, and error if error occur. param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1. In linux, use /bin/bash -c to execute command, In windows, use powershell.exe to execute command.
+Execute shell command, return the stdout and stderr string of command, and error if error occur. param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1. In linux, use /bin/bash -c to execute command, In windows, use powershell.exe to execute command. +The second parameter of the function is the cmd option control parameter. The type is func(*exec.Cmd). You can set the cmd attribute through this parameter.
Signature: @@ -263,7 +264,9 @@ import ( func main() { // linux or mac - stdout, stderr, err := system.ExecCommand("ls") + stdout, stderr, err := system.ExecCommand("ls", func(cmd *exec.Cmd) { + cmd.Dir = "/tmp" + }) fmt.Println("std out: ", stdout) fmt.Println("std err: ", stderr) assert.Equal("", stderr) diff --git a/formatter/byte.go b/formatter/byte.go index 10ac341..2f02ccb 100644 --- a/formatter/byte.go +++ b/formatter/byte.go @@ -18,63 +18,63 @@ import ( // http://en.wikipedia.org/wiki/Binary_prefix const ( // Decimal - UnitB = 1 - UnitKB = 1000 - UnitMB = 1000 * UnitKB - UnitGB = 1000 * UnitMB - UnitTB = 1000 * UnitGB - UnitPB = 1000 * UnitTB - UnitEB = 1000 * UnitPB + unitB = 1 + unitKB = 1000 + unitMB = 1000 * unitKB + unitGB = 1000 * unitMB + unitTB = 1000 * unitGB + unitPB = 1000 * unitTB + unitEB = 1000 * unitPB // Binary - UnitBiB = 1 - UnitKiB = 1024 - UnitMiB = 1024 * UnitKiB - UnitGiB = 1024 * UnitMiB - UnitTiB = 1024 * UnitGiB - UnitPiB = 1024 * UnitTiB - UnitEiB = 1024 * UnitPiB + unitBiB = 1 + unitKiB = 1024 + unitMiB = 1024 * unitKiB + unitGiB = 1024 * unitMiB + unitTiB = 1024 * unitGiB + unitPiB = 1024 * unitTiB + unitEiB = 1024 * unitPiB ) // type byteUnitMap map[byte]int64 var ( decimalByteMap = map[string]uint64{ - "b": UnitB, - "kb": UnitKB, - "mb": UnitMB, - "gb": UnitGB, - "tb": UnitTB, - "pb": UnitPB, - "eb": UnitEB, + "b": unitB, + "kb": unitKB, + "mb": unitMB, + "gb": unitGB, + "tb": unitTB, + "pb": unitPB, + "eb": unitEB, // Without suffix - "": UnitB, - "k": UnitKB, - "m": UnitMB, - "g": UnitGB, - "t": UnitTB, - "p": UnitPB, - "e": UnitEB, + "": unitB, + "k": unitKB, + "m": unitMB, + "g": unitGB, + "t": unitTB, + "p": unitPB, + "e": unitEB, } binaryByteMap = map[string]uint64{ - "bi": UnitBiB, - "kib": UnitKiB, - "mib": UnitMiB, - "gib": UnitGiB, - "tib": UnitTiB, - "pib": UnitPiB, - "eib": UnitEiB, + "bi": unitBiB, + "kib": unitKiB, + "mib": unitMiB, + "gib": unitGiB, + "tib": unitTiB, + "pib": unitPiB, + "eib": unitEiB, // Without suffix - "": UnitBiB, - "ki": UnitKiB, - "mi": UnitMiB, - "gi": UnitGiB, - "ti": UnitTiB, - "pi": UnitPiB, - "ei": UnitEiB, + "": unitBiB, + "ki": unitKiB, + "mi": unitMiB, + "gi": unitGiB, + "ti": unitTiB, + "pi": unitPiB, + "ei": unitEiB, } ) diff --git a/formatter/byte_test.go b/formatter/byte_test.go index 6bc50d4..897dec2 100644 --- a/formatter/byte_test.go +++ b/formatter/byte_test.go @@ -15,11 +15,11 @@ func TestDecimalBytes(t *testing.T) { assert.Equal("1.024KB", DecimalBytes(1024)) assert.Equal("1.2346MB", DecimalBytes(1234567)) assert.Equal("1.235MB", DecimalBytes(1234567, 3)) - assert.Equal("1.123GB", DecimalBytes(float64(1.123*UnitGB))) - assert.Equal("2.123TB", DecimalBytes(float64(2.123*UnitTB))) - assert.Equal("3.123PB", DecimalBytes(float64(3.123*UnitPB))) - assert.Equal("4.123EB", DecimalBytes(float64(4.123*UnitEB))) - assert.Equal("1EB", DecimalBytes(float64(1000*UnitPB))) + assert.Equal("1.123GB", DecimalBytes(float64(1.123*unitGB))) + assert.Equal("2.123TB", DecimalBytes(float64(2.123*unitTB))) + assert.Equal("3.123PB", DecimalBytes(float64(3.123*unitPB))) + assert.Equal("4.123EB", DecimalBytes(float64(4.123*unitEB))) + assert.Equal("1EB", DecimalBytes(float64(1000*unitPB))) assert.Equal("62MB", DecimalBytes(61812496, 0)) assert.Equal("61.8MB", DecimalBytes(61812496, 1)) assert.Equal("401MB", DecimalBytes(401000000)) diff --git a/maputil/map.go b/maputil/map.go index d0d9d58..e60222e 100644 --- a/maputil/map.go +++ b/maputil/map.go @@ -453,3 +453,23 @@ func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V { 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 +} diff --git a/maputil/map_example_test.go b/maputil/map_example_test.go index e385367..9b9751a 100644 --- a/maputil/map_example_test.go +++ b/maputil/map_example_test.go @@ -540,3 +540,295 @@ func ExampleGetOrSet() { // a // 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] +} + +func ExampleOrderedMap_Set() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} + +func ExampleOrderedMap_Get() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + val1, ok := om.Get("a") + fmt.Println(val1, ok) + + val2, ok := om.Get("d") + fmt.Println(val2, ok) + + // Output: + // 1 true + // 0 false +} + +func ExampleOrderedMap_Front() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + frontElement, ok := om.Front() + fmt.Println(frontElement) + fmt.Println(ok) + + // Output: + // {a 1} + // true +} + +func ExampleOrderedMap_Back() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + backElement, ok := om.Back() + fmt.Println(backElement) + fmt.Println(ok) + + // Output: + // {c 3} + // true +} + +func ExampleOrderedMap_Delete() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Delete("b") + + fmt.Println(om.Keys()) + + // Output: + // [a c] +} + +func ExampleOrderedMap_Clear() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Clear() + + fmt.Println(om.Keys()) + + // Output: + // [] +} + +func ExampleOrderedMap_Len() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Len() + + fmt.Println(om.Len()) + + // Output: + // 3 +} + +func ExampleOrderedMap_Keys() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + keys := om.Keys() + + fmt.Println(keys) + + // Output: + // [a b c] +} + +func ExampleOrderedMap_Values() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + values := om.Values() + + fmt.Println(values) + + // Output: + // [1 2 3] +} + +func ExampleOrderedMap_Contains() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + result1 := om.Contains("a") + result2 := om.Contains("d") + + fmt.Println(result1) + fmt.Println(result2) + + // Output: + // true + // false +} + +func ExampleOrderedMap_Range() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + om.Range(func(key string, value int) bool { + fmt.Println(key, value) + return true + }) + + // Output: + // a 1 + // b 2 + // c 3 +} + +func ExampleOrderedMap_Elements() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + elements := om.Elements() + + fmt.Println(elements) + + // Output: + // [{a 1} {b 2} {c 3}] +} + +func ExampleOrderedMap_Iter() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.Iter() { + fmt.Println(elem) + } + + // Output: + // {a 1} + // {b 2} + // {c 3} +} + +func ExampleOrderedMap_ReverseIter() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + for elem := range om.ReverseIter() { + fmt.Println(elem) + } + + // Output: + // {c 3} + // {b 2} + // {a 1} +} + +func ExampleOrderedMap_SortByKey() { + om := NewOrderedMap[int, string]() + + om.Set(3, "c") + om.Set(1, "a") + om.Set(4, "d") + om.Set(2, "b") + + om.SortByKey(func(a, b int) bool { + return a < b + }) + + fmt.Println(om.Elements()) + + // Output: + // [{1 a} {2 b} {3 c} {4 d}] +} + +func ExampleOrderedMap_MarshalJSON() { + om := NewOrderedMap[string, int]() + + om.Set("a", 1) + om.Set("b", 2) + om.Set("c", 3) + + b, _ := om.MarshalJSON() + + fmt.Println(string(b)) + + // Output: + // {"a":1,"b":2,"c":3} +} + +func ExampleOrderedMap_UnmarshalJSON() { + om := NewOrderedMap[string, int]() + + data := []byte(`{"a":1,"b":2,"c":3}`) + + om.UnmarshalJSON(data) + + fmt.Println(om.Elements()) + + // Output: + // [{a 1} {b 2} {c 3}] +} diff --git a/maputil/map_test.go b/maputil/map_test.go index 94b8a92..90b33bb 100644 --- a/maputil/map_test.go +++ b/maputil/map_test.go @@ -707,3 +707,47 @@ func TestGetOrSet(t *testing.T) { assert.Equal("a", result1) 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) +} diff --git a/maputil/orderedmap.go b/maputil/orderedmap.go new file mode 100644 index 0000000..b62d381 --- /dev/null +++ b/maputil/orderedmap.go @@ -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), + } +} + +// Sets the given key-value pair. +// 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 + } +} diff --git a/maputil/orderedmap_test.go b/maputil/orderedmap_test.go new file mode 100644 index 0000000..46ecff0 --- /dev/null +++ b/maputil/orderedmap_test.go @@ -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")) +} diff --git a/slice/slice_test.go b/slice/slice_test.go index 4fd9da2..5a59080 100644 --- a/slice/slice_test.go +++ b/slice/slice_test.go @@ -16,12 +16,20 @@ func TestContain(t *testing.T) { assert := internal.NewAssert(t, "TestContain") - assert.Equal(true, Contain([]string{"a", "b", "c"}, "a")) - assert.Equal(false, Contain([]string{"a", "b", "c"}, "d")) - assert.Equal(true, Contain([]string{""}, "")) - assert.Equal(false, Contain([]string{}, "")) + tests := []struct { + slice []string + give 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) { @@ -30,32 +38,53 @@ func TestContainBy(t *testing.T) { assert := internal.NewAssert(t, "TestContainBy") type foo struct { - A string - B int + a string + b int } - array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}} - result1 := ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 }) - result2 := ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 }) + tests := []struct { + slice []foo + 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"} - result3 := ContainBy(array2, func(t string) bool { return t == "a" }) - 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) + for _, tt := range tests { + assert.Equal(tt.want, ContainBy(tt.slice, tt.predicateFn)) + } } func TestContainSubSlice(t *testing.T) { t.Parallel() 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"})) - assert.Equal(true, ContainSubSlice([]int{1, 2, 3}, []int{1, 2})) - assert.Equal(false, ContainSubSlice([]int{1, 2, 3}, []int{0, 1})) + + tests := []struct { + slice []string + 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) { @@ -63,29 +92,24 @@ func TestChunk(t *testing.T) { 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)) - - 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)) + for _, tt := range tests { + assert.Equal(tt.want, Chunk(tt.slice, tt.chuanSize)) + } } func TestCompact(t *testing.T) { @@ -134,6 +158,7 @@ func TestEqual(t *testing.T) { assert.Equal(true, Equal(slice1, slice2)) assert.Equal(false, Equal(slice1, slice3)) + assert.Equal(false, Equal(slice2, slice3)) } // go test -fuzz=Fuzz -fuzztime=10s . @@ -163,12 +188,27 @@ func TestEvery(t *testing.T) { assert := internal.NewAssert(t, "TestEvery") - nums := []int{1, 2, 3, 5} isEven := func(i, num int) bool { 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) { @@ -176,12 +216,27 @@ func TestNone(t *testing.T) { assert := internal.NewAssert(t, "TestNone") - nums := []int{1, 2, 3, 5} - check := func(i, num int) bool { + isEven := func(i, num int) bool { + return num%2 == 0 + } + isOdd := func(i, num int) bool { 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) { @@ -189,12 +244,27 @@ func TestSome(t *testing.T) { assert := internal.NewAssert(t, "TestSome") - nums := []int{1, 2, 3, 5} - hasEven := func(i, num int) bool { + isEven := func(i, num int) bool { 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) { @@ -202,33 +272,37 @@ func TestFilter(t *testing.T) { assert := internal.NewAssert(t, "TestFilter") - nums := []int{1, 2, 3, 4, 5} - isEven := func(i, num int) bool { - return num%2 == 0 - } + t.Run("filter int slice", func(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + isEven := func(i, num int) bool { + return num%2 == 0 + } - assert.Equal([]int{2, 4}, Filter(nums, isEven)) + assert.Equal([]int{2, 4}, Filter(nums, isEven)) + }) - type student struct { - name string - age int - } - students := []student{ - {"a", 10}, - {"b", 11}, - {"c", 12}, - {"d", 13}, - {"e", 14}, - } - studentsOfAageGreat12 := []student{ - {"d", 13}, - {"e", 14}, - } - filterFunc := func(i int, s student) bool { - return s.age > 12 - } + t.Run("filter struct slice", func(t *testing.T) { + type student struct { + name string + age int + } + students := []student{ + {"a", 10}, + {"b", 11}, + {"c", 12}, + {"d", 13}, + {"e", 14}, + } + studentsOfAgeGreat12 := []student{ + {"d", 13}, + {"e", 14}, + } + filterFunc := func(i int, s student) bool { + return s.age > 12 + } - assert.Equal(studentsOfAageGreat12, Filter(students, filterFunc)) + assert.Equal(studentsOfAgeGreat12, Filter(students, filterFunc)) + }) } func TestGroupBy(t *testing.T) { @@ -249,6 +323,8 @@ func TestGroupBy(t *testing.T) { func TestGroupWith(t *testing.T) { t.Parallel() + assert := internal.NewAssert(t, "TestGroupWith") + nums := []float64{6.1, 4.2, 6.3} floor := func(num float64) float64 { return math.Floor(num) @@ -257,9 +333,8 @@ func TestGroupWith(t *testing.T) { 4: {4.2}, 6: {6.1, 6.3}, } - actual := GroupWith(nums, floor) - assert := internal.NewAssert(t, "TestGroupWith") - assert.Equal(expected, actual) + + assert.Equal(expected, GroupWith(nums, floor)) } func TestCount(t *testing.T) { @@ -295,8 +370,15 @@ func TestFind(t *testing.T) { result, ok := Find(nums, even) assert := internal.NewAssert(t, "TestFind") + assert.Equal(true, ok) assert.Equal(2, *result) + + _, ok = Find(nums, func(_ int, v int) bool { + return v == 6 + }) + + assert.Equal(false, ok) } func TestFindBy(t *testing.T) { @@ -358,19 +440,6 @@ func TestFindLast(t *testing.T) { 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) { 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 { return agg + item }) + assert.Equal(10, result1) result2 := ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string { return agg + fmt.Sprintf("%v", item) }) - - assert.Equal(10, result1) assert.Equal("1234", result2) - } func TestReduceRight(t *testing.T) { @@ -692,15 +759,23 @@ func TestDeleteAt(t *testing.T) { t.Parallel() assert := internal.NewAssert(t, "TestDeleteAt") - arr := []int{1, 2, 3, 4, 5} - assert.Equal([]int{2, 3, 4, 5}, DeleteAt(arr, 0)) - assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 4)) + tests := []struct { + 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)) - assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 6)) - - assert.Equal([]int{1, 2, 3, 4, 5}, arr) + for _, tt := range tests { + assert.Equal(tt.wang, DeleteAt(tt.slice, tt.deletePos)) + } } func TestDeleteRange(t *testing.T) { @@ -720,16 +795,24 @@ func TestDrop(t *testing.T) { assert := internal.NewAssert(t, "TestDrop") - assert.Equal([]int{}, Drop([]int{}, 0)) - assert.Equal([]int{}, Drop([]int{}, 1)) - assert.Equal([]int{}, Drop([]int{}, -1)) + tests := []struct { + slice []int + 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)) - assert.Equal([]int{2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 1)) - 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)) + for _, tt := range tests { + assert.Equal(tt.want, Drop(tt.slice, tt.dropNum)) + } } func TestDropRight(t *testing.T) { @@ -737,16 +820,23 @@ func TestDropRight(t *testing.T) { assert := internal.NewAssert(t, "TestDropRight") - assert.Equal([]int{}, DropRight([]int{}, 0)) - assert.Equal([]int{}, DropRight([]int{}, 1)) - assert.Equal([]int{}, DropRight([]int{}, -1)) - - 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)) - assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 5)) - assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 6)) - - assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, -1)) + tests := []struct { + slice []int + 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{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) { @@ -754,22 +844,19 @@ func TestDropWhile(t *testing.T) { 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 { - return n != 2 - }) - 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) + for _, tt := range tests { + assert.Equal(tt.want, DropWhile(tt.slice, tt.fn)) + } } func TestDropRightWhile(t *testing.T) { @@ -777,22 +864,19 @@ func TestDropRightWhile(t *testing.T) { 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 { - return n != 2 - }) - 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) + for _, tt := range tests { + assert.Equal(tt.want, DropRightWhile(tt.slice, tt.fn)) + } } func TestInsertAt(t *testing.T) { @@ -800,15 +884,25 @@ func TestInsertAt(t *testing.T) { assert := internal.NewAssert(t, "TestInsertAt") - strs := []string{"a", "b", "c"} - assert.Equal([]string{"a", "b", "c"}, InsertAt(strs, -1, "1")) - assert.Equal([]string{"a", "b", "c"}, InsertAt(strs, 4, "1")) - assert.Equal([]string{"1", "a", "b", "c"}, InsertAt(strs, 0, "1")) - assert.Equal([]string{"a", "1", "b", "c"}, InsertAt(strs, 1, "1")) - assert.Equal([]string{"a", "b", "1", "c"}, InsertAt(strs, 2, "1")) - assert.Equal([]string{"a", "b", "c", "1"}, InsertAt(strs, 3, "1")) - assert.Equal([]string{"1", "2", "3", "a", "b", "c"}, InsertAt(strs, 0, []string{"1", "2", "3"})) - assert.Equal([]string{"a", "b", "c", "1", "2", "3"}, InsertAt(strs, 3, []string{"1", "2", "3"})) + tests := []struct { + slice []string + insertPos int + insertValue any + want []string + }{ + {[]string{"a", "b", "c"}, -1, "1", []string{"a", "b", "c"}}, + {[]string{"a", "b", "c"}, 4, "1", []string{"a", "b", "c"}}, + {[]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) {