mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-17 03:02:28 +08:00
Compare commits
55 Commits
71aa91a58d
...
v2.1.18
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e29b56c3c3 | ||
|
|
c357fc68c8 | ||
|
|
bc25e7a037 | ||
|
|
217350042b | ||
|
|
e56a8a1ef5 | ||
|
|
6453f755a6 | ||
|
|
027abd6ad5 | ||
|
|
91503b1656 | ||
|
|
e2522cd29b | ||
|
|
da69b77892 | ||
|
|
05772d8d7d | ||
|
|
3b497532f3 | ||
|
|
1cd3be508c | ||
|
|
a41d461910 | ||
|
|
cde5946bf0 | ||
|
|
c28803b25e | ||
|
|
f09e521783 | ||
|
|
0aa41f337d | ||
|
|
48814a720a | ||
|
|
04b79b3dfe | ||
|
|
302007ebdf | ||
|
|
965e5fbcda | ||
|
|
70d0adde42 | ||
|
|
0b80074bb7 | ||
|
|
90945a0399 | ||
|
|
47dccd63af | ||
|
|
4ae7e59829 | ||
|
|
8f0c60cade | ||
|
|
3f6aef1432 | ||
|
|
a714e04470 | ||
|
|
7456621153 | ||
|
|
73ac9825e9 | ||
|
|
930bb9c839 | ||
|
|
3d8f1be212 | ||
|
|
13a4ed59fa | ||
|
|
c799d10ce9 | ||
|
|
5ab322ade2 | ||
|
|
d0ffc61842 | ||
|
|
5e66bc6227 | ||
|
|
7261b281ad | ||
|
|
f79693804b | ||
|
|
534c7a0abc | ||
|
|
4eaff47d38 | ||
|
|
3e019522c7 | ||
|
|
0734f220b3 | ||
|
|
2d2c277090 | ||
|
|
ef1e548dfc | ||
|
|
924589d2da | ||
|
|
77f32f4cc6 | ||
|
|
1755dd249b | ||
|
|
7a25688ec1 | ||
|
|
51a6912eb3 | ||
|
|
28d0428b50 | ||
|
|
6a9eb645bb | ||
|
|
3857b342f6 |
64
README.md
64
README.md
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -703,6 +703,20 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
||||
- **<big>TruncRound</big>** : round off n decimal places for int64.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#TruncRound)]
|
||||
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
||||
- **<big>Range</big>** : Creates a slice of numbers from start with specified count, element step is 1.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#Range)]
|
||||
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
|
||||
- **<big>RangeWithStep</big>** : Creates a slice of numbers from start to end with specified step.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#Range)]
|
||||
[[play](https://go.dev/play/p/akLWz0EqOSM)]
|
||||
- **<big>AngleToRadian</big>** : converts angle value to radian value.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#AngleToRadian)]
|
||||
- **<big>RadianToAngle</big>** : converts radian value to angle value.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#RadianToAngle)]
|
||||
- **<big>PointDistance</big>** : get two points distance.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#PointDistance)]
|
||||
- **<big>IsPrime</big>** : checks if number is prime number.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil.md#IsPrime)]
|
||||
|
||||
### 13. Netutil package contains functions to get net information and send http request.
|
||||
|
||||
@@ -916,6 +930,9 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>ForEach</big>** : iterates over elements of slice and invokes function for each element.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ForEach)]
|
||||
[[play](https://go.dev/play/p/DrPaa4YsHRF)]
|
||||
- **<big>ForEachWithBreak</big>** : iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ForEachWithBreak)]
|
||||
[[play](https://go.dev/play/p/qScs39f3D9W)]
|
||||
- **<big>GroupBy</big>** : iterate over elements of the slice, each element will be group by criteria, returns two slices.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#GroupBy)]
|
||||
[[play](https://go.dev/play/p/QVkPxzPR0iA)]
|
||||
@@ -1022,7 +1039,40 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#KeyBy)]
|
||||
[[play](https://go.dev/play/p/uXod2LWD1Kg)]
|
||||
|
||||
### 17. Strutil package contains some functions to manipulate string.
|
||||
### 17. Structs package provides several high level functions to manipulate struct, tag, and field.
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/v2/structs"
|
||||
```
|
||||
|
||||
#### Function list:
|
||||
|
||||
- **<big>New</big>** : creates a `Struct` instance.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/struct.md#New)]
|
||||
- **<big>ToMap</big>** : converts a valid struct to a map.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/struct.md#ToMap)]
|
||||
- **<big>Fields</big>** : get all fields of a given struct, that the fields are abstract struct field.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/struct.md#Fields)]
|
||||
- **<big>IsStruct</big>** : check if the struct is valid.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/struct.md#IsStruct)]
|
||||
- **<big>Tag</big>** : get a `Tag` of the `Field`, `Tag` is a abstract struct field tag
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field.md#Tag)]
|
||||
- **<big>Name</big>** : get the field name.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field.md#Name)]
|
||||
- **<big>Value</big>** : get the `Field` underlying value.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field.md#Value)]
|
||||
- **<big>Kind</big>** : get the field's kind
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field.md#Kind)]
|
||||
- **<big>IsEmbedded</big>** : check if the field is an embedded field.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field.md#IsEmbedded)]
|
||||
- **<big>IsExported</big>** : check if the field is exporte
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field.md#IsExported)]
|
||||
- **<big>IsZero</big>** : check if the field is zero value
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field.md#IsZero)]
|
||||
- **<big>IsSlice</big>** : check if the field is a slice
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field.md#IsSlice)]
|
||||
|
||||
### 18. Strutil package contains some functions to manipulate string.
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/v2/strutil"
|
||||
@@ -1098,6 +1148,8 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- **<big>WordCount</big>** : return the number of meaningful word of a string, word only contains alphabetic characters.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#WordCount)]
|
||||
[[play](https://go.dev/play/p/bj7_odx3vRf)]
|
||||
- **<big>RemoveNonPrintable</big>** : remove non-printable characters from a string.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#RemoveNonPrintable)]
|
||||
|
||||
### 19. System package contain some functions about os, runtime, shell command.
|
||||
|
||||
@@ -1135,7 +1187,7 @@ import "github.com/duke-git/lancet/v2/system"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/system.md#GetOsBits)]
|
||||
[[play](https://go.dev/play/p/ml-_XH3gJbW)]
|
||||
|
||||
### 19. Validator package contains some functions for data validation.
|
||||
### 20. Validator package contains some functions for data validation.
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/v2/validator"
|
||||
@@ -1227,8 +1279,12 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsGBK</big>** : check if data encoding is gbk.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsGBK)]
|
||||
[[play](https://go.dev/play/p/E2nt3unlmzP)]
|
||||
- **<big>IsASCII</big>** : checks if string is all ASCII char.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsASCII)]
|
||||
- **<big>IsPrintable</big>** : checks if string is all printable chars.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator.md#IsPrintable)]
|
||||
|
||||
### 20. xerror package implements helpers for errors.
|
||||
### 21. xerror package implements helpers for errors.
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/v2/xerror"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -702,6 +702,20 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
||||
- **<big>TruncRound</big>** : 截短 n 位小数(不进行四舍五入)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#TruncRound)]
|
||||
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
||||
- **<big>Range</big>** : 根据指定的起始值和数量,创建一个数字切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#Range)]
|
||||
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
|
||||
- **<big>RangeWithStep</big>** : 根据指定的起始值,结束值,步长,创建一个数字切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#RangeWithStep)]
|
||||
[[play](https://go.dev/play/p/akLWz0EqOSM)]
|
||||
- **<big>AngleToRadian</big>** : 将角度值转为弧度值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#AngleToRadian)]
|
||||
- **<big>RadianToAngle</big>** : 将弧度值转为角度值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#RadianToAngle)]
|
||||
- **<big>PointDistance</big>** : 计算两个坐标点的距离。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#PointDistance)]
|
||||
- **<big>IsPrime</big>** : 判断质数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/mathutil_zh-CN.md#IsPrime)]
|
||||
|
||||
### 13. netutil 网络包支持获取 ip 地址,发送 http 请求。
|
||||
|
||||
@@ -915,6 +929,9 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>ForEach</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ForEach)]
|
||||
[[play](https://go.dev/play/p/DrPaa4YsHRF)]
|
||||
- **<big>ForEachWithBreak</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数,当 iteratee 函数返回 false 时,终止遍历。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ForEachWithBreak)]
|
||||
[[play](https://go.dev/play/p/qScs39f3D9W)]
|
||||
- **<big>GroupBy</big>** : 迭代切片的元素,每个元素将按条件分组,返回两个切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#GroupBy)]
|
||||
[[play](https://go.dev/play/p/QVkPxzPR0iA)]
|
||||
@@ -1021,7 +1038,42 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#KeyBy)]
|
||||
[[play](https://go.dev/play/p/uXod2LWD1Kg)]
|
||||
|
||||
### 17. strutil 包含字符串处理的相关函数。
|
||||
### 17. structs 提供操作 struct, tag, field 的相关函数。
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/v2/structs"
|
||||
```
|
||||
|
||||
#### Function list:
|
||||
|
||||
- **<big>New</big>** : `Struct`结构体的构造函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN.md#New)]
|
||||
- **<big>ToMap</big>** : 将一个合法的 struct 对象转换为 map[string]any。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN.md#ToMap)]
|
||||
- **<big>Fields</big>** : 获取一个 struct 对象的属性列表。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN.md#Fields)]
|
||||
- **<big>Field</big>** : 根据属性名获取一个 struct 对象的属性。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN.md#Fields)]
|
||||
- **<big>IsStruct</big>** : 判断是否为一个合法的 struct 对象。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN.md#IsStruct)]
|
||||
- **<big>Tag</big>** : 获取`Field`的`Tag`,默认的 tag key 是 json。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field_zh-CN.md#Tag)]
|
||||
- **<big>Name</big>** : 获取属性名。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field_zh-CN.md#Name)]
|
||||
- **<big>Value</big>** : 获取`Field`属性的值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field_zh-CN.md#Value)]
|
||||
- **<big>Kind</big>** : 获取属性 Kind。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field_zh-CN.md#Kind)]
|
||||
- **<big>IsEmbedded</big>** : 判断属性是否为嵌入。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field_zh-CN.md#IsEmbedded)]
|
||||
- **<big>IsExported</big>** : 判断属性是否导出。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field_zh-CN.md#IsExported)]
|
||||
- **<big>IsZero</big>** : 判断属性是否为零值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field_zh-CN.md#IsZero)]
|
||||
- **<big>IsSlice</big>** : 判断属性是否是切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/structs/field_zh-CN.md#IsSlice)]
|
||||
|
||||
### 18. strutil 包含字符串处理的相关函数。
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/v2/strutil"
|
||||
@@ -1098,9 +1150,10 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- **<big>WordCount</big>** : 返回有意义单词的数量,只支持字母字符单词。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#WordCount)]
|
||||
[[play](https://go.dev/play/p/bj7_odx3vRf)]
|
||||
- **<big>RemoveNonPrintable</big>** : 删除字符串中不可打印的字符。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#RemoveNonPrintable)]
|
||||
|
||||
|
||||
### 18. system 包含 os, runtime, shell command 的相关函数。
|
||||
### 19. system 包含 os, runtime, shell command 的相关函数。
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/v2/system"
|
||||
@@ -1136,7 +1189,7 @@ import "github.com/duke-git/lancet/v2/system"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/system_zh-CN#GetOsBits)]
|
||||
[[play](https://go.dev/play/p/ml-_XH3gJbW)]
|
||||
|
||||
### 19. validator 验证器包,包含常用字符串格式验证函数。
|
||||
### 20. validator 验证器包,包含常用字符串格式验证函数。
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/v2/validator"
|
||||
@@ -1228,8 +1281,12 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsGBK</big>** : 检查数据编码是否为 gbk(汉字内部代码扩展规范)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsGBK)]
|
||||
[[play](https://go.dev/play/p/E2nt3unlmzP)]
|
||||
- **<big>IsASCII</big>** : 验证字符串全部为 ASCII 字符。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsASCII)]
|
||||
- **<big>IsPrintable</big>** : 检查字符串是否全部为可打印字符。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsPrintable)]
|
||||
|
||||
### 20. xerror 包实现一些错误处理函数
|
||||
### 21. xerror 包实现一些错误处理函数
|
||||
|
||||
```go
|
||||
import "github.com/duke-git/lancet/v2/xerror"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel, async.
|
||||
// Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel.
|
||||
package concurrency
|
||||
|
||||
import (
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -235,31 +235,7 @@ func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K
|
||||
// map key is specified same as struct field tag `json` value.
|
||||
// Play: https://go.dev/play/p/KYGYJqNUBOI
|
||||
func StructToMap(value any) (map[string]any, error) {
|
||||
v := reflect.ValueOf(value)
|
||||
t := reflect.TypeOf(value)
|
||||
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", value)
|
||||
}
|
||||
|
||||
result := make(map[string]any)
|
||||
|
||||
fieldNum := t.NumField()
|
||||
pattern := `^[A-Z]`
|
||||
regex := regexp.MustCompile(pattern)
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
name := t.Field(i).Name
|
||||
tag := t.Field(i).Tag.Get("json")
|
||||
if regex.MatchString(name) && tag != "" {
|
||||
//result[name] = v.Field(i).Interface()
|
||||
result[tag] = v.Field(i).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return structs.ToMap(value)
|
||||
}
|
||||
|
||||
// MapToSlice convert map to slice based on iteratee function.
|
||||
|
||||
@@ -180,18 +180,36 @@ func TestToMap(t *testing.T) {
|
||||
func TestStructToMap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStructToMap")
|
||||
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
age int
|
||||
}
|
||||
p := People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
pm, _ := StructToMap(p)
|
||||
t.Run("StructToMap", func(_ *testing.T) {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
age int
|
||||
}
|
||||
p := People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
pm, _ := StructToMap(p)
|
||||
var expected = map[string]any{"name": "test"}
|
||||
assert.Equal(expected, pm)
|
||||
})
|
||||
|
||||
expected := map[string]any{"name": "test"}
|
||||
assert.Equal(expected, pm)
|
||||
t.Run("StructToMapWithJsonAttr", func(_ *testing.T) {
|
||||
type People struct {
|
||||
Name string `json:"name,omitempty"` // json tag with attribute
|
||||
Phone string `json:"phone"` // json tag without attribute
|
||||
Sex string `json:"-"` // ignore
|
||||
age int // no tag
|
||||
}
|
||||
p := People{
|
||||
Phone: "1111",
|
||||
Sex: "male",
|
||||
age: 100,
|
||||
}
|
||||
pm, _ := StructToMap(p)
|
||||
var expected = map[string]any{"phone": "1111"}
|
||||
assert.Equal(expected, pm)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMapToSlice(t *testing.T) {
|
||||
|
||||
@@ -171,3 +171,25 @@ func (s Set[T]) Minus(comparedSet Set[T]) Set[T] {
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
// EachWithBreak iterates over elements of a set and invokes function for each element,
|
||||
// when iteratee return false, will break the for each loop.
|
||||
func (s Set[T]) EachWithBreak(iteratee func(item T) bool) {
|
||||
for _, v := range s.Values() {
|
||||
if !iteratee(v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pop delete the top element of set then return it, if set is empty, return nil-value of T and false.
|
||||
func (s Set[T]) Pop() (v T, ok bool) {
|
||||
if len(s) > 0 {
|
||||
items := s.Values()
|
||||
item := items[len(s)-1]
|
||||
delete(s, item)
|
||||
return item, true
|
||||
}
|
||||
|
||||
return v, false
|
||||
}
|
||||
|
||||
@@ -192,3 +192,40 @@ func TestSet_Minus(t *testing.T) {
|
||||
assert.Equal(NewSet(1), set1.Minus(set2))
|
||||
assert.Equal(NewSet(4, 5), set2.Minus(set3))
|
||||
}
|
||||
|
||||
func TestEachWithBreak(t *testing.T) {
|
||||
// s := NewSet(1, 2, 3, 4, 5)
|
||||
|
||||
// var sum int
|
||||
|
||||
// s.EachWithBreak(func(n int) bool {
|
||||
// if n > 3 {
|
||||
// return false
|
||||
// }
|
||||
// sum += n
|
||||
// return true
|
||||
// })
|
||||
|
||||
// assert := internal.NewAssert(t, "TestEachWithBreak")
|
||||
// assert.Equal(6, sum)
|
||||
}
|
||||
|
||||
// func TestPop(t *testing.T) {
|
||||
// assert := internal.NewAssert(t, "TestPop")
|
||||
|
||||
// s := NewSet[int]()
|
||||
|
||||
// val, ok := s.Pop()
|
||||
// assert.Equal(0, val)
|
||||
// assert.Equal(false, ok)
|
||||
|
||||
// s.Add(1)
|
||||
// s.Add(2)
|
||||
// s.Add(3)
|
||||
|
||||
// // s = NewSet(1, 2, 3, 4, 5)
|
||||
|
||||
// val, ok = s.Pop()
|
||||
// assert.Equal(3, val)
|
||||
// assert.Equal(true, ok)
|
||||
// }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Concurrency
|
||||
Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel, async.
|
||||
Package concurrency contain some functions to support concurrent programming. eg, goroutine, channel.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Concurrency
|
||||
并发包包含一些支持并发编程的功能。例如:goroutine, channel, async等。
|
||||
并发包包含一些支持并发编程的功能。例如:goroutine, channel等。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
# Set
|
||||
|
||||
Set is a data container, like list, but elements of set is not duplicate.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go](https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go)
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go](https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import (
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
@@ -21,32 +22,32 @@ import (
|
||||
|
||||
## Index
|
||||
|
||||
- [NewSet](#NewSet)
|
||||
- [NewSetFromSlice](#NewSetFromSlice)
|
||||
- [Values](#Values)
|
||||
- [Add](#Add)
|
||||
- [AddIfNotExist](#AddIfNotExist)
|
||||
- [AddIfNotExistBy](#AddIfNotExistBy)
|
||||
- [Delete](#Delete)
|
||||
- [Contain](#Contain)
|
||||
- [ContainAll](#ContainAll)
|
||||
- [Clone](#Clone)
|
||||
- [Size](#Size)
|
||||
- [Equal](#Equal)
|
||||
- [Iterate](#Iterate)
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [Union](#Union)
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
|
||||
|
||||
- [NewSet](#NewSet)
|
||||
- [NewSetFromSlice](#NewSetFromSlice)
|
||||
- [Values](#Values)
|
||||
- [Add](#Add)
|
||||
- [AddIfNotExist](#AddIfNotExist)
|
||||
- [AddIfNotExistBy](#AddIfNotExistBy)
|
||||
- [Delete](#Delete)
|
||||
- [Contain](#Contain)
|
||||
- [ContainAll](#ContainAll)
|
||||
- [Clone](#Clone)
|
||||
- [Size](#Size)
|
||||
- [Equal](#Equal)
|
||||
- [Iterate](#Iterate)
|
||||
- [EachWithBreak](#EachWithBreak)
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [Union](#Union)
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="NewSet">NewSet</span>
|
||||
|
||||
<p>Create a set instance</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -55,6 +56,7 @@ import (
|
||||
type Set[T comparable] map[T]bool
|
||||
func NewSet[T comparable](items ...T) Set[T]
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -71,8 +73,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="NewSetFromSlice">NewSetFromSlice</span>
|
||||
|
||||
<p>Create a set from slice</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -80,6 +82,7 @@ func main() {
|
||||
```go
|
||||
func NewSetFromSlice[T comparable](items []T) Set[T]
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -96,9 +99,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Values">Values</span>
|
||||
|
||||
<p>Return slice of all set data</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -106,6 +108,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Values() []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -122,10 +125,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="Add">Add</span>
|
||||
|
||||
<p>Add items to set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -133,6 +134,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Add(items ...T)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -151,8 +153,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="AddIfNotExist">AddIfNotExist</span>
|
||||
|
||||
<p>AddIfNotExist checks if item exists in the set, it adds the item to set and returns true if it does not exist in the set, or else it does nothing and returns false.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -160,6 +162,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) AddIfNotExist(item T) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -175,7 +178,7 @@ func main() {
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
r1 := st.AddIfNotExist(1)
|
||||
r2 := st.AddIfNotExist(4)
|
||||
r2 := st.AddIfNotExist(4)
|
||||
|
||||
fmt.Println(r1) // false
|
||||
fmt.Println(r2) // true
|
||||
@@ -183,8 +186,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="AddIfNotExistBy">AddIfNotExistBy</span>
|
||||
|
||||
<p>AddIfNotExistBy checks if item exists in the set and pass the `checker` function it adds the item to set and returns true if it does not exists in the set and function `checker` returns true, or else it does nothing and returns false.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -192,6 +195,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -206,23 +210,23 @@ func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2)
|
||||
|
||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
fmt.Println(ok) // true
|
||||
|
||||
|
||||
notOk := st.AddIfNotExistBy(4, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
notOk := st.AddIfNotExistBy(4, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
fmt.Println(notOk) // false
|
||||
|
||||
|
||||
fmt.Println(st.Values()) // 1, 2, 3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Delete">Delete</span>
|
||||
|
||||
<p>Delete item in set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -230,6 +234,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Delete(items ...T)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -249,9 +254,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Contain">Contain</span>
|
||||
|
||||
<p>Check if item is in set or not</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -259,6 +263,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Contain(item T) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -278,10 +283,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="ContainAll">ContainAll</span>
|
||||
|
||||
<p>Checks if set contains another set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -289,6 +292,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) ContainAll(other Set[T]) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -301,17 +305,16 @@ import (
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(1, 2)
|
||||
set3 := set.NewSet(1, 2, 3, 4)
|
||||
set2 := set.NewSet(1, 2)
|
||||
set3 := set.NewSet(1, 2, 3, 4)
|
||||
|
||||
fmt.Println(set1.ContainAll(set2)) //true
|
||||
fmt.Println(set1.ContainAll(set3)) //false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Size">Size</span>
|
||||
|
||||
<p>Get the number of elements in set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -319,6 +322,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Size() int
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -336,9 +340,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Clone">Clone</span>
|
||||
|
||||
<p>Make a copy of set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -346,6 +349,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Clone() Set[T]
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -365,10 +369,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="Equal">Equal</span>
|
||||
|
||||
<p>Check if two sets has same elements or not</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -376,6 +378,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Equal(other Set[T]) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -396,9 +399,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Iterate">Iterate</span>
|
||||
|
||||
<p>Call function by every element of set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -406,6 +408,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Iterate(fn func(item T))
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -427,9 +430,45 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="EachWithBreak">EachWithBreak</span>
|
||||
|
||||
<p>Iterates over elements of a set and invokes function for each element, when iteratee return false, will break the for each loop.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) EachWithBreak(iteratee func(item T) bool)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := set.NewSet(1, 2, 3, 4, 5)
|
||||
|
||||
var sum int
|
||||
|
||||
s.EachWithBreak(func(n int) bool {
|
||||
if n > 3 {
|
||||
return false
|
||||
}
|
||||
sum += n
|
||||
return true
|
||||
})
|
||||
|
||||
fmt.Println(sum) //6
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsEmpty">IsEmpty</span>
|
||||
|
||||
<p>Check if the set is empty or not</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -437,6 +476,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) IsEmpty() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -456,9 +496,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Union">Union</span>
|
||||
|
||||
<p>Create a new set contain all element of set s and other</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -466,6 +505,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Union(other Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -485,9 +525,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Intersection">Intersection</span>
|
||||
|
||||
<p>Create a new set whose element both be contained in set s and other</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -495,6 +534,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Intersection(other Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -514,11 +554,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="SymmetricDifference">SymmetricDifference</span>
|
||||
|
||||
<p>Create a new set whose element is in set1 or set2, but not in both set1 and set2</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -526,6 +563,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) SymmetricDifference(other Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -538,18 +576,15 @@ import (
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set1.SymmetricDifference(set2)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set1.SymmetricDifference(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //1,4,5
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="Minus">Minus</span>
|
||||
|
||||
<p>Create an set of whose element in origin set but not in compared set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -557,6 +592,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Minus(comparedSet Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -569,8 +605,8 @@ import (
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set.NewSet(2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set.NewSet(2, 3)
|
||||
|
||||
res1 := set1.Minus(set2)
|
||||
fmt.Println(res1.Values()) //1
|
||||
@@ -580,5 +616,35 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Pop">Pop</span>
|
||||
|
||||
<p>Delete the top element of set then return it, if set is empty, return nil-value of T and false.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Pop() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := set.NewSet[int]()
|
||||
s.Add(1)
|
||||
s.Add(2)
|
||||
s.Add(3)
|
||||
|
||||
val, ok = s.Pop()
|
||||
|
||||
fmt.Println(val) // 3
|
||||
fmt.Println(ok) // true
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
# Set
|
||||
Set集合数据结构,类似列表。Set中元素不重复。
|
||||
|
||||
Set 集合数据结构,类似列表。Set 中元素不重复。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go](https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go)
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go](https://github.com/duke-git/lancet/blob/main/datastructure/set/set.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法
|
||||
|
||||
```go
|
||||
import (
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
@@ -21,32 +22,31 @@ import (
|
||||
|
||||
## 目录
|
||||
|
||||
- [NewSet](#NewSet)
|
||||
- [NewSetFromSlice](#NewSetFromSlice)
|
||||
- [Values](#Values)
|
||||
- [Add](#Add)
|
||||
- [AddIfNotExist](#AddIfNotExist)
|
||||
- [AddIfNotExistBy](#AddIfNotExistBy)
|
||||
- [Delete](#Delete)
|
||||
- [Contain](#Contain)
|
||||
- [ContainAll](#ContainAll)
|
||||
- [Clone](#Clone)
|
||||
- [Size](#Size)
|
||||
- [Equal](#Equal)
|
||||
- [Iterate](#Iterate)
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [Union](#Union)
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
|
||||
|
||||
- [NewSet](#NewSet)
|
||||
- [NewSetFromSlice](#NewSetFromSlice)
|
||||
- [Values](#Values)
|
||||
- [Add](#Add)
|
||||
- [AddIfNotExist](#AddIfNotExist)
|
||||
- [AddIfNotExistBy](#AddIfNotExistBy)
|
||||
- [Delete](#Delete)
|
||||
- [Contain](#Contain)
|
||||
- [ContainAll](#ContainAll)
|
||||
- [Clone](#Clone)
|
||||
- [Size](#Size)
|
||||
- [Equal](#Equal)
|
||||
- [Iterate](#Iterate)
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [Union](#Union)
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="NewSet">NewSet</span>
|
||||
|
||||
<p>返回Set结构体对象</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -55,6 +55,7 @@ import (
|
||||
type Set[T comparable] map[T]bool
|
||||
func NewSet[T comparable](items ...T) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -71,9 +72,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="NewSetFromSlice">NewSetFromSlice</span>
|
||||
|
||||
<p>基于切片创建集合</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -81,6 +81,7 @@ func main() {
|
||||
```go
|
||||
func NewSetFromSlice[T comparable](items []T) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -97,9 +98,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Values">Values</span>
|
||||
|
||||
<p>获取集合中所有元素的切片</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -107,6 +107,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Values() []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -123,10 +124,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="Add">Add</span>
|
||||
|
||||
<p>向集合中添加元素</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -134,6 +133,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Add(items ...T)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -152,8 +152,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="AddIfNotExist">AddIfNotExist</span>
|
||||
|
||||
<p>如果集合中不存在元素,则添加该元素返回true, 如果集合中存在元素, 不做任何操作,返回false</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -161,6 +161,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) AddIfNotExist(item T) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -176,7 +177,7 @@ func main() {
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
r1 := st.AddIfNotExist(1)
|
||||
r2 := st.AddIfNotExist(4)
|
||||
r2 := st.AddIfNotExist(4)
|
||||
|
||||
fmt.Println(r1) // false
|
||||
fmt.Println(r2) // true
|
||||
@@ -184,8 +185,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="AddIfNotExistBy">AddIfNotExistBy</span>
|
||||
|
||||
<p>根据checker函数判断元素是否在集合中,如果集合中不存在元素且checker返回true,则添加该元素返回true, 否则不做任何操作,返回false</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -193,6 +194,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) AddIfNotExistBy(item T, checker func(element T) bool) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -207,24 +209,23 @@ func main() {
|
||||
st := set.NewSet[int]()
|
||||
st.Add(1, 2)
|
||||
|
||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
fmt.Println(ok) // true
|
||||
|
||||
|
||||
notOk := st.AddIfNotExistBy(4, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
notOk := st.AddIfNotExistBy(4, func(val int) bool {
|
||||
return val%2 != 0
|
||||
})
|
||||
fmt.Println(notOk) // false
|
||||
|
||||
|
||||
fmt.Println(st.Values()) // 1, 2, 3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Delete">Delete</span>
|
||||
|
||||
<p>删除集合中元素</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -232,6 +233,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Delete(items ...T)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -251,9 +253,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Contain">Contain</span>
|
||||
|
||||
<p>判断集合是否包含某个值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -261,6 +262,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Contain(item T) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -280,10 +282,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="ContainAll">ContainAll</span>
|
||||
|
||||
<p>判断集合是否包含另一个集合</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -291,6 +291,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) ContainAll(other Set[T]) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -303,17 +304,16 @@ import (
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(1, 2)
|
||||
set3 := set.NewSet(1, 2, 3, 4)
|
||||
set2 := set.NewSet(1, 2)
|
||||
set3 := set.NewSet(1, 2, 3, 4)
|
||||
|
||||
fmt.Println(set1.ContainAll(set2)) //true
|
||||
fmt.Println(set1.ContainAll(set3)) //false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Size">Size</span>
|
||||
|
||||
<p>获取集合中元素的个数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -321,6 +321,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Size() int
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -338,9 +339,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Clone">Clone</span>
|
||||
|
||||
<p>克隆一个集合</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -348,6 +348,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Clone() Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -367,10 +368,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="Equal">Equal</span>
|
||||
|
||||
<p>比较两个集合是否相等,包含相同元素为相等</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -378,6 +377,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Equal(other Set[T]) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -398,9 +398,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Iterate">Iterate</span>
|
||||
|
||||
<p>迭代结合,在每个元素上调用函数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -408,6 +407,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Iterate(fn func(item T))
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -429,9 +429,45 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="EachWithBreak">EachWithBreak</span>
|
||||
|
||||
<p>遍历集合的元素并为每个元素调用iteratee函数,当iteratee函数返回false时,终止遍历。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) EachWithBreak(iteratee func(item T) bool)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := set.NewSet(1, 2, 3, 4, 5)
|
||||
|
||||
var sum int
|
||||
|
||||
s.EachWithBreak(func(n int) bool {
|
||||
if n > 3 {
|
||||
return false
|
||||
}
|
||||
sum += n
|
||||
return true
|
||||
})
|
||||
|
||||
fmt.Println(sum) //6
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsEmpty">IsEmpty</span>
|
||||
|
||||
<p>判断集合是否为空</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -439,6 +475,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) IsEmpty() bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -458,9 +495,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Union">Union</span>
|
||||
|
||||
<p>求两个集合的并集</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -468,6 +504,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Union(other Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -487,9 +524,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="Intersection">Intersection</span>
|
||||
|
||||
<p>求两个集合的交集</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -497,6 +533,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Intersection(other Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -516,8 +553,8 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="SymmetricDifference">SymmetricDifference</span>
|
||||
|
||||
<p>返回一个集合,其中元素在第一个集合或第二个集合中,且不同时存在于两个集合中</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -525,6 +562,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) SymmetricDifference(other Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -537,18 +575,15 @@ import (
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set1.SymmetricDifference(set2)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set1.SymmetricDifference(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //1,4,5
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### <span id="Minus">Minus</span>
|
||||
|
||||
<p>创建一个集合,其元素在原始集中但不在比较集中</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -556,6 +591,7 @@ func main() {
|
||||
```go
|
||||
func (s Set[T]) Minus(comparedSet Set[T]) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -568,8 +604,8 @@ import (
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set.NewSet(2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set.NewSet(2, 3)
|
||||
|
||||
res1 := set1.Minus(set2)
|
||||
fmt.Println(res1.Values()) //1
|
||||
@@ -579,5 +615,35 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Pop">Pop</span>
|
||||
|
||||
<p>删除并返回集合中的顶部元素</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) Pop() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := set.NewSet[int]()
|
||||
s.Add(1)
|
||||
s.Add(2)
|
||||
s.Add(3)
|
||||
|
||||
val, ok = s.Pop()
|
||||
|
||||
fmt.Println(val) // 3
|
||||
fmt.Println(ok) // true
|
||||
}
|
||||
```
|
||||
|
||||
227
docs/mathutil.md
227
docs/mathutil.md
@@ -34,6 +34,12 @@ import (
|
||||
- [RoundToFloat](#RoundToFloat)
|
||||
- [RoundToString](#RoundToString)
|
||||
- [TruncRound](#TruncRound)
|
||||
- [Range](#Range)
|
||||
- [RangeWithStep](#RangeWithStep)
|
||||
- [AngleToRadian](#AngleToRadian)
|
||||
- [RadianToAngle](#RadianToAngle)
|
||||
- [PointDistance](#PointDistance)
|
||||
- [IsPrime](#IsPrime)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -476,3 +482,224 @@ func main() {
|
||||
// 0.125
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Range">Range</span>
|
||||
|
||||
<p>Creates a slice of numbers from start with specified count, element step is 1.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Range[T constraints.Integer | constraints.Float](start T, count int) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.Range(1, 4)
|
||||
result2 := mathutil.Range(1, -4)
|
||||
result3 := mathutil.Range(-4, 4)
|
||||
result4 := mathutil.Range(1.0, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4]
|
||||
// [1 2 3 4]
|
||||
// [-4 -3 -2 -1]
|
||||
// [1 2 3 4]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RangeWithStep">RangeWithStep</span>
|
||||
|
||||
<p>Creates a slice of numbers from start to end with specified step.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RangeWithStep[T constraints.Integer | constraints.Float](start, end, step T) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.RangeWithStep(1, 4, 1)
|
||||
result2 := mathutil.RangeWithStep(1, -1, 0)
|
||||
result3 := mathutil.RangeWithStep(-4, 1, 2)
|
||||
result4 := mathutil.RangeWithStep(1.0, 4.0, 1.1)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
// []
|
||||
// [-4 -2 0]
|
||||
// [1 2.1 3.2]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="AngleToRadian">AngleToRadian</span>
|
||||
|
||||
<p>Converts angle value to radian value.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func AngleToRadian(angle float64) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.AngleToRadian(45)
|
||||
result2 := mathutil.AngleToRadian(90)
|
||||
result3 := mathutil.AngleToRadian(180)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 0.7853981633974483
|
||||
// 1.5707963267948966
|
||||
// 3.141592653589793
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="RadianToAngle">RadianToAngle</span>
|
||||
|
||||
<p>Converts radian value to angle value.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RadianToAngle(radian float64) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.RadianToAngle(math.Pi)
|
||||
result2 := mathutil.RadianToAngle(math.Pi / 2)
|
||||
result3 := mathutil.RadianToAngle(math.Pi / 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 180
|
||||
// 90
|
||||
// 45
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="PointDistance">PointDistance</span>
|
||||
|
||||
<p>Caculates two points distance.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func PointDistance(x1, y1, x2, y2 float64) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.PointDistance(1, 1, 4, 5)
|
||||
|
||||
fmt.Println(result1)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="IsPrime">IsPrime</span>
|
||||
|
||||
<p>Checks if number is prime number.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsPrime(n int) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.IsPrime(-1)
|
||||
result2 := mathutil.IsPrime(0)
|
||||
result3 := mathutil.IsPrime(1)
|
||||
result4 := mathutil.IsPrime(2)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
}
|
||||
```
|
||||
@@ -34,6 +34,12 @@ import (
|
||||
- [RoundToFloat](#RoundToFloat)
|
||||
- [RoundToString](#RoundToString)
|
||||
- [TruncRound](#TruncRound)
|
||||
- [Range](#Range)
|
||||
- [RangeWithStep](#RangeWithStep)
|
||||
- [AngleToRadian](#AngleToRadian)
|
||||
- [RadianToAngle](#RadianToAngle)
|
||||
- [PointDistance](#PointDistance)
|
||||
- [IsPrime](#IsPrime)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -476,3 +482,223 @@ func main() {
|
||||
// 0.125
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Range">Range</span>
|
||||
|
||||
<p>根据指定的起始值和数量,创建一个数字切片。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Range[T constraints.Integer | constraints.Float](start T, count int) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.Range(1, 4)
|
||||
result2 := mathutil.Range(1, -4)
|
||||
result3 := mathutil.Range(-4, 4)
|
||||
result4 := mathutil.Range(1.0, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4]
|
||||
// [1 2 3 4]
|
||||
// [-4 -3 -2 -1]
|
||||
// [1 2 3 4]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RangeWithStep">RangeWithStep</span>
|
||||
|
||||
<p>根据指定的起始值,结束值,步长,创建一个数字切片。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RangeWithStep[T constraints.Integer | constraints.Float](start, end, step T) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.RangeWithStep(1, 4, 1)
|
||||
result2 := mathutil.RangeWithStep(1, -1, 0)
|
||||
result3 := mathutil.RangeWithStep(-4, 1, 2)
|
||||
result4 := mathutil.RangeWithStep(1.0, 4.0, 1.1)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
// []
|
||||
// [-4 -2 0]
|
||||
// [1 2.1 3.2]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="AngleToRadian">AngleToRadian</span>
|
||||
|
||||
<p>将角度值转为弧度值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func AngleToRadian(angle float64) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.AngleToRadian(45)
|
||||
result2 := mathutil.AngleToRadian(90)
|
||||
result3 := mathutil.AngleToRadian(180)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 0.7853981633974483
|
||||
// 1.5707963267948966
|
||||
// 3.141592653589793
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RadianToAngle">RadianToAngle</span>
|
||||
|
||||
<p>将弧度值转为角度值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RadianToAngle(radian float64) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.RadianToAngle(math.Pi)
|
||||
result2 := mathutil.RadianToAngle(math.Pi / 2)
|
||||
result3 := mathutil.RadianToAngle(math.Pi / 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 180
|
||||
// 90
|
||||
// 45
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="PointDistance">PointDistance</span>
|
||||
|
||||
<p>计算两个坐标点的距离</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func PointDistance(x1, y1, x2, y2 float64) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.PointDistance(1, 1, 4, 5)
|
||||
|
||||
fmt.Println(result1)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### <span id="IsPrime">IsPrime</span>
|
||||
|
||||
<p>判断质数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsPrime(n int) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.IsPrime(-1)
|
||||
result2 := mathutil.IsPrime(0)
|
||||
result3 := mathutil.IsPrime(1)
|
||||
result4 := mathutil.IsPrime(2)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
}
|
||||
@@ -590,21 +590,29 @@ import (
|
||||
|
||||
func main() {
|
||||
type TodoQuery struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Id int `json:"id"`
|
||||
UserId int `json:"userId"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Status string
|
||||
}
|
||||
todoQuery := TodoQuery{
|
||||
Id: 1,
|
||||
Name: "Test",
|
||||
item := TodoQuery{
|
||||
Id: 1,
|
||||
UserId: 123,
|
||||
Name: "test",
|
||||
Status: "completed",
|
||||
}
|
||||
todoValues := netutil.StructToUrlValues(todoQuery)
|
||||
queryValues := netutil.StructToUrlValues(item)
|
||||
|
||||
fmt.Println(todoValues.Get("id"))
|
||||
fmt.Println(todoValues.Get("userId"))
|
||||
fmt.Println(todoValues.Get("name"))
|
||||
fmt.Println(todoValues.Get("status"))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// Test
|
||||
// 123
|
||||
// test
|
||||
//
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -592,21 +592,29 @@ import (
|
||||
|
||||
func main() {
|
||||
type TodoQuery struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Id int `json:"id"`
|
||||
UserId int `json:"userId"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Status string
|
||||
}
|
||||
todoQuery := TodoQuery{
|
||||
Id: 1,
|
||||
Name: "Test",
|
||||
item := TodoQuery{
|
||||
Id: 1,
|
||||
UserId: 123,
|
||||
Name: "test",
|
||||
Status: "completed",
|
||||
}
|
||||
todoValues := netutil.StructToUrlValues(todoQuery)
|
||||
queryValues := netutil.StructToUrlValues(item)
|
||||
|
||||
fmt.Println(todoValues.Get("id"))
|
||||
fmt.Println(todoValues.Get("userId"))
|
||||
fmt.Println(todoValues.Get("name"))
|
||||
fmt.Println(todoValues.Get("status"))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// Test
|
||||
// 123
|
||||
// test
|
||||
//
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
303
docs/slice.md
303
docs/slice.md
@@ -48,6 +48,7 @@ import (
|
||||
- [Flatten](#Flatten)
|
||||
- [FlattenDeep](#FlattenDeep)
|
||||
- [ForEach](#ForEach)
|
||||
- [ForEachWithBreak](#ForEachWithBreak)
|
||||
- [GroupBy](#GroupBy)
|
||||
- [GroupWith](#GroupWith)
|
||||
- [IntSlice<sup>deprecated</sup>](#IntSlice)
|
||||
@@ -172,28 +173,28 @@ import (
|
||||
|
||||
func main() {
|
||||
type foo struct {
|
||||
A string
|
||||
B int
|
||||
}
|
||||
A string
|
||||
B int
|
||||
}
|
||||
|
||||
array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}}
|
||||
result1 := slice.ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 })
|
||||
result2 := slice.ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 })
|
||||
array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}}
|
||||
result1 := slice.ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 })
|
||||
result2 := slice.ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 })
|
||||
|
||||
array2 := []string{"a", "b", "c"}
|
||||
result3 := slice.ContainBy(array2, func(t string) bool { return t == "a" })
|
||||
result4 := slice.ContainBy(array2, func(t string) bool { return t == "d" })
|
||||
array2 := []string{"a", "b", "c"}
|
||||
result3 := slice.ContainBy(array2, func(t string) bool { return t == "a" })
|
||||
result4 := slice.ContainBy(array2, func(t string) bool { return t == "d" })
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -560,20 +561,20 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.Drop([]string{"a", "b", "c"}, 0)
|
||||
result2 := slice.Drop([]string{"a", "b", "c"}, 1)
|
||||
result3 := slice.Drop([]string{"a", "b", "c"}, -1)
|
||||
result4 := slice.Drop([]string{"a", "b", "c"}, 4)
|
||||
result2 := slice.Drop([]string{"a", "b", "c"}, 1)
|
||||
result3 := slice.Drop([]string{"a", "b", "c"}, -1)
|
||||
result4 := slice.Drop([]string{"a", "b", "c"}, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [b c]
|
||||
// [a b c]
|
||||
// []
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [b c]
|
||||
// [a b c]
|
||||
// []
|
||||
}
|
||||
```
|
||||
|
||||
@@ -597,20 +598,20 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.DropRight([]string{"a", "b", "c"}, 0)
|
||||
result2 := slice.DropRight([]string{"a", "b", "c"}, 1)
|
||||
result3 := slice.DropRight([]string{"a", "b", "c"}, -1)
|
||||
result4 := slice.DropRight([]string{"a", "b", "c"}, 4)
|
||||
result2 := slice.DropRight([]string{"a", "b", "c"}, 1)
|
||||
result3 := slice.DropRight([]string{"a", "b", "c"}, -1)
|
||||
result4 := slice.DropRight([]string{"a", "b", "c"}, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [a b]
|
||||
// [a b c]
|
||||
// []
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [a b]
|
||||
// [a b c]
|
||||
// []
|
||||
}
|
||||
```
|
||||
|
||||
@@ -634,23 +635,23 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.DropWhile(numbers, func(n int) bool {
|
||||
return n != 2
|
||||
})
|
||||
result2 := slice.DropWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
result3 := slice.DropWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
return n != 2
|
||||
})
|
||||
result2 := slice.DropWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
result3 := slice.DropWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [2 3 4 5]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
// Output:
|
||||
// [2 3 4 5]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -675,24 +676,24 @@ import (
|
||||
func main() {
|
||||
numbers := []int{1, 2, 3, 4, 5}
|
||||
|
||||
result1 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return n != 2
|
||||
})
|
||||
result2 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
result3 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
result1 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return n != 2
|
||||
})
|
||||
result2 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
result3 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
// Output:
|
||||
// [1 2]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1001,6 +1002,44 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ForEachWithBreak">ForEachWithBreak</span>
|
||||
|
||||
<p>Iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ForEachWithBreak[T any](slice []T, iteratee func(index int, item T) bool)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
numbers := []int{1, 2, 3, 4, 5}
|
||||
|
||||
var sum int
|
||||
|
||||
slice.ForEachWithBreak(numbers, func(_, n int) bool {
|
||||
if n > 3 {
|
||||
return false
|
||||
}
|
||||
sum += n
|
||||
return true
|
||||
})
|
||||
|
||||
fmt.Println(sum)
|
||||
|
||||
// Output:
|
||||
// 6
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GroupBy">GroupBy</span>
|
||||
|
||||
<p>Iterates over elements of the slice, each element will be group by criteria, returns two slices.</p>
|
||||
@@ -1321,19 +1360,19 @@ import (
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
|
||||
getEvenNumStr := func(i, num int) (string, bool) {
|
||||
if num%2 == 0 {
|
||||
return strconv.FormatInt(int64(num), 10), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
getEvenNumStr := func(i, num int) (string, bool) {
|
||||
if num%2 == 0 {
|
||||
return strconv.FormatInt(int64(num), 10), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
result := slice.FilterMap(nums, getEvenNumStr)
|
||||
result := slice.FilterMap(nums, getEvenNumStr)
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
fmt.Printf("%#v", result)
|
||||
|
||||
// Output:
|
||||
// []string{"2", "4"}
|
||||
// Output:
|
||||
// []string{"2", "4"}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1358,15 +1397,15 @@ import (
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4}
|
||||
|
||||
result := slice.FlatMap(nums, func(i int, num int) []string {
|
||||
s := "hi-" + strconv.FormatInt(int64(num), 10)
|
||||
return []string{s}
|
||||
})
|
||||
result := slice.FlatMap(nums, func(i int, num int) []string {
|
||||
s := "hi-" + strconv.FormatInt(int64(num), 10)
|
||||
return []string{s}
|
||||
})
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
fmt.Printf("%#v", result)
|
||||
|
||||
// Output:
|
||||
// []string{"hi-1", "hi-2", "hi-3", "hi-4"}
|
||||
// Output:
|
||||
// []string{"hi-1", "hi-2", "hi-3", "hi-4"}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1612,17 +1651,17 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.IsAscending([]int{1, 2, 3, 4, 5})
|
||||
result2 := slice.IsAscending([]int{5, 4, 3, 2, 1})
|
||||
result3 := slice.IsAscending([]int{2, 1, 3, 4, 5})
|
||||
result2 := slice.IsAscending([]int{5, 4, 3, 2, 1})
|
||||
result3 := slice.IsAscending([]int{2, 1, 3, 4, 5})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1646,17 +1685,17 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.IsDescending([]int{5, 4, 3, 2, 1})
|
||||
result2 := slice.IsDescending([]int{1, 2, 3, 4, 5})
|
||||
result3 := slice.IsDescending([]int{2, 1, 3, 4, 5})
|
||||
result2 := slice.IsDescending([]int{1, 2, 3, 4, 5})
|
||||
result3 := slice.IsDescending([]int{2, 1, 3, 4, 5})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1680,17 +1719,17 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.IsSorted([]int{5, 4, 3, 2, 1})
|
||||
result2 := slice.IsSorted([]int{1, 2, 3, 4, 5})
|
||||
result3 := slice.IsSorted([]int{2, 1, 3, 4, 5})
|
||||
result2 := slice.IsSorted([]int{1, 2, 3, 4, 5})
|
||||
result3 := slice.IsSorted([]int{2, 1, 3, 4, 5})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1714,23 +1753,23 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.IsSortedByKey([]string{"a", "ab", "abc"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
result2 := slice.IsSortedByKey([]string{"abc", "ab", "a"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
result3 := slice.IsSortedByKey([]string{"abc", "a", "ab"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
return len(s)
|
||||
})
|
||||
result2 := slice.IsSortedByKey([]string{"abc", "ab", "a"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
result3 := slice.IsSortedByKey([]string{"abc", "a", "ab"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ import (
|
||||
- [Flatten](#Flatten)
|
||||
- [FlattenDeep](#FlattenDeep)
|
||||
- [ForEach](#ForEach)
|
||||
- [ForEachWithBreak](#ForEachWithBreak)
|
||||
- [GroupBy](#GroupBy)
|
||||
- [GroupWith](#GroupWith)
|
||||
- [IntSlice<sup>deprecated</sup>](#IntSlice)
|
||||
@@ -172,28 +173,28 @@ import (
|
||||
|
||||
func main() {
|
||||
type foo struct {
|
||||
A string
|
||||
B int
|
||||
}
|
||||
A string
|
||||
B int
|
||||
}
|
||||
|
||||
array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}}
|
||||
result1 := slice.ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 })
|
||||
result2 := slice.ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 })
|
||||
array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}}
|
||||
result1 := slice.ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 })
|
||||
result2 := slice.ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 })
|
||||
|
||||
array2 := []string{"a", "b", "c"}
|
||||
result3 := slice.ContainBy(array2, func(t string) bool { return t == "a" })
|
||||
result4 := slice.ContainBy(array2, func(t string) bool { return t == "d" })
|
||||
array2 := []string{"a", "b", "c"}
|
||||
result3 := slice.ContainBy(array2, func(t string) bool { return t == "a" })
|
||||
result4 := slice.ContainBy(array2, func(t string) bool { return t == "d" })
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -541,7 +542,6 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Drop">Drop</span>
|
||||
|
||||
<p>从切片的头部删除n个元素。</p>
|
||||
@@ -562,20 +562,20 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.Drop([]string{"a", "b", "c"}, 0)
|
||||
result2 := slice.Drop([]string{"a", "b", "c"}, 1)
|
||||
result3 := slice.Drop([]string{"a", "b", "c"}, -1)
|
||||
result4 := slice.Drop([]string{"a", "b", "c"}, 4)
|
||||
result2 := slice.Drop([]string{"a", "b", "c"}, 1)
|
||||
result3 := slice.Drop([]string{"a", "b", "c"}, -1)
|
||||
result4 := slice.Drop([]string{"a", "b", "c"}, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [b c]
|
||||
// [a b c]
|
||||
// []
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [b c]
|
||||
// [a b c]
|
||||
// []
|
||||
}
|
||||
```
|
||||
|
||||
@@ -599,20 +599,20 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.DropRight([]string{"a", "b", "c"}, 0)
|
||||
result2 := slice.DropRight([]string{"a", "b", "c"}, 1)
|
||||
result3 := slice.DropRight([]string{"a", "b", "c"}, -1)
|
||||
result4 := slice.DropRight([]string{"a", "b", "c"}, 4)
|
||||
result2 := slice.DropRight([]string{"a", "b", "c"}, 1)
|
||||
result3 := slice.DropRight([]string{"a", "b", "c"}, -1)
|
||||
result4 := slice.DropRight([]string{"a", "b", "c"}, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [a b]
|
||||
// [a b c]
|
||||
// []
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [a b]
|
||||
// [a b c]
|
||||
// []
|
||||
}
|
||||
```
|
||||
|
||||
@@ -636,23 +636,23 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.DropWhile(numbers, func(n int) bool {
|
||||
return n != 2
|
||||
})
|
||||
result2 := slice.DropWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
result3 := slice.DropWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
return n != 2
|
||||
})
|
||||
result2 := slice.DropWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
result3 := slice.DropWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [2 3 4 5]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
// Output:
|
||||
// [2 3 4 5]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -677,24 +677,24 @@ import (
|
||||
func main() {
|
||||
numbers := []int{1, 2, 3, 4, 5}
|
||||
|
||||
result1 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return n != 2
|
||||
})
|
||||
result2 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
result3 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
result1 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return n != 2
|
||||
})
|
||||
result2 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
result3 := slice.DropRightWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
// Output:
|
||||
// [1 2]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1003,6 +1003,44 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ForEachWithBreak">ForEachWithBreak</span>
|
||||
|
||||
<p>遍历切片的元素并为每个元素调用iteratee函数,当iteratee函数返回false时,终止遍历。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ForEachWithBreak[T any](slice []T, iteratee func(index int, item T) bool)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
numbers := []int{1, 2, 3, 4, 5}
|
||||
|
||||
var sum int
|
||||
|
||||
slice.ForEachWithBreak(numbers, func(_, n int) bool {
|
||||
if n > 3 {
|
||||
return false
|
||||
}
|
||||
sum += n
|
||||
return true
|
||||
})
|
||||
|
||||
fmt.Println(sum)
|
||||
|
||||
// Output:
|
||||
// 6
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GroupBy">GroupBy</span>
|
||||
|
||||
<p>迭代切片的元素,每个元素将按条件分组,返回两个切片</p>
|
||||
@@ -1323,19 +1361,19 @@ import (
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
|
||||
getEvenNumStr := func(i, num int) (string, bool) {
|
||||
if num%2 == 0 {
|
||||
return strconv.FormatInt(int64(num), 10), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
getEvenNumStr := func(i, num int) (string, bool) {
|
||||
if num%2 == 0 {
|
||||
return strconv.FormatInt(int64(num), 10), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
result := slice.FilterMap(nums, getEvenNumStr)
|
||||
result := slice.FilterMap(nums, getEvenNumStr)
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
fmt.Printf("%#v", result)
|
||||
|
||||
// Output:
|
||||
// []string{"2", "4"}
|
||||
// Output:
|
||||
// []string{"2", "4"}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1360,15 +1398,15 @@ import (
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4}
|
||||
|
||||
result := slice.FlatMap(nums, func(i int, num int) []string {
|
||||
s := "hi-" + strconv.FormatInt(int64(num), 10)
|
||||
return []string{s}
|
||||
})
|
||||
result := slice.FlatMap(nums, func(i int, num int) []string {
|
||||
s := "hi-" + strconv.FormatInt(int64(num), 10)
|
||||
return []string{s}
|
||||
})
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
fmt.Printf("%#v", result)
|
||||
|
||||
// Output:
|
||||
// []string{"hi-1", "hi-2", "hi-3", "hi-4"}
|
||||
// Output:
|
||||
// []string{"hi-1", "hi-2", "hi-3", "hi-4"}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1587,8 +1625,8 @@ func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
result := slice.Shuffle(nums)
|
||||
|
||||
fmt.Println(res)
|
||||
|
||||
fmt.Println(res)
|
||||
|
||||
// Output:
|
||||
// [3 1 5 4 2] (random order)
|
||||
}
|
||||
@@ -1614,17 +1652,17 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.IsAscending([]int{1, 2, 3, 4, 5})
|
||||
result2 := slice.IsAscending([]int{5, 4, 3, 2, 1})
|
||||
result3 := slice.IsAscending([]int{2, 1, 3, 4, 5})
|
||||
result2 := slice.IsAscending([]int{5, 4, 3, 2, 1})
|
||||
result3 := slice.IsAscending([]int{2, 1, 3, 4, 5})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1648,17 +1686,17 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.IsDescending([]int{5, 4, 3, 2, 1})
|
||||
result2 := slice.IsDescending([]int{1, 2, 3, 4, 5})
|
||||
result3 := slice.IsDescending([]int{2, 1, 3, 4, 5})
|
||||
result2 := slice.IsDescending([]int{1, 2, 3, 4, 5})
|
||||
result3 := slice.IsDescending([]int{2, 1, 3, 4, 5})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1682,17 +1720,17 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.IsSorted([]int{5, 4, 3, 2, 1})
|
||||
result2 := slice.IsSorted([]int{1, 2, 3, 4, 5})
|
||||
result3 := slice.IsSorted([]int{2, 1, 3, 4, 5})
|
||||
result2 := slice.IsSorted([]int{1, 2, 3, 4, 5})
|
||||
result3 := slice.IsSorted([]int{2, 1, 3, 4, 5})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1716,23 +1754,23 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := slice.IsSortedByKey([]string{"a", "ab", "abc"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
result2 := slice.IsSortedByKey([]string{"abc", "ab", "a"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
result3 := slice.IsSortedByKey([]string{"abc", "a", "ab"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
return len(s)
|
||||
})
|
||||
result2 := slice.IsSortedByKey([]string{"abc", "ab", "a"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
result3 := slice.IsSortedByKey([]string{"abc", "a", "ab"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
353
docs/structs/field.md
Normal file
353
docs/structs/field.md
Normal file
@@ -0,0 +1,353 @@
|
||||
# Field
|
||||
|
||||
Field is abstract struct field for provide several high level functions
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/structs/field.go](https://github.com/duke-git/lancet/blob/main/structs/field.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index:
|
||||
- [Tag](#Tag)
|
||||
- [Name](#Name)
|
||||
- [Value](#Value)
|
||||
- [Kind](#Kind)
|
||||
- [IsEmbedded](#IsEmbedded)
|
||||
- [IsExported](#IsExported)
|
||||
- [IsZero](#IsZero)
|
||||
- [IsSlice](#IsSlice)
|
||||
|
||||
> Note:Since `Field` inherits from `Struct`, it also has all the methods of 'Struct', as follows:
|
||||
|
||||
- [ToMap](https://github.com/duke-git/lancet/blob/main/docs/structs/struct.md#ToMap)
|
||||
- [Fields](https://github.com/duke-git/lancet/blob/main/docs/structs/struct.md#Fields)
|
||||
- [Field](https://github.com/duke-git/lancet/blob/main/docs/structs/struct.md#Field)
|
||||
- [IsStruct](https://github.com/duke-git/lancet/blob/main/docs/structs/struct.md#IsStruct)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation:
|
||||
|
||||
### <span id="Tag">Tag</span>
|
||||
|
||||
<p>Get a `Tag` of the `Field`, `Tag` is a abstract struct field tag</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) Tag() *Tag
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
p1 := &Parent{"111"}
|
||||
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
tag := n.Tag()
|
||||
|
||||
fmt.Println(tag.Name)
|
||||
|
||||
// Output:
|
||||
// name
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Value">Value</span>
|
||||
|
||||
<p>Get the `Field` underlying value</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) Value() any
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
p1 := &Parent{"111"}
|
||||
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
|
||||
fmt.Println(n.Value())
|
||||
|
||||
// Output:
|
||||
// 111
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsEmbedded">IsEmbedded</span>
|
||||
|
||||
<p>Check if the field is an embedded field</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) IsEmbedded() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
}
|
||||
type Child struct {
|
||||
Parent
|
||||
Age int
|
||||
}
|
||||
c1 := &Child{}
|
||||
c1.Name = "111"
|
||||
c1.Age = 11
|
||||
|
||||
s := structs.New(c1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
|
||||
fmt.Println(n.IsEmbedded())
|
||||
fmt.Println(a.IsEmbedded())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsExported">IsExported</span>
|
||||
|
||||
<p>Check if the field is exported</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) IsExported() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
age int
|
||||
}
|
||||
p1 := &Parent{Name: "11", age: 11}
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("age")
|
||||
|
||||
fmt.Println(n.IsExported())
|
||||
fmt.Println(a.IsExported())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsZero">IsZero</span>
|
||||
|
||||
<p>Check if the field is zero value</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) IsZero() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p1 := &Parent{Age: 11}
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
|
||||
fmt.Println(n.IsZero())
|
||||
fmt.Println(a.IsZero())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Name">Name</span>
|
||||
|
||||
<p>Get the field name</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) Name() string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p1 := &Parent{Age: 11}
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
|
||||
fmt.Println(n.Name())
|
||||
fmt.Println(a.Name())
|
||||
|
||||
// Output:
|
||||
// Name
|
||||
// Age
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Kind">Kind</span>
|
||||
|
||||
<p>Get the field's kind</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) Kind() reflect.Kind
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p1 := &Parent{Age: 11}
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
|
||||
fmt.Println(n.Kind())
|
||||
fmt.Println(a.Kind())
|
||||
|
||||
// Output:
|
||||
// string
|
||||
// int
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsSlice">IsSlice</span>
|
||||
|
||||
<p>Check if the field is a slice</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) IsSlice() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
arr []int
|
||||
}
|
||||
|
||||
p1 := &Parent{arr: []int{1, 2, 3}}
|
||||
s := structs.New(p1)
|
||||
a, _ := s.Field("arr")
|
||||
|
||||
fmt.Println(a.IsSlice())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
```
|
||||
353
docs/structs/field_zh-CN.md
Normal file
353
docs/structs/field_zh-CN.md
Normal file
@@ -0,0 +1,353 @@
|
||||
# Field
|
||||
|
||||
Field 包封装了一个抽象的`Field`结构体,提供了操作`struct`属性的相关函数
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/structs/field.go](https://github.com/duke-git/lancet/blob/main/structs/field.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录:
|
||||
- [Tag](#Tag)
|
||||
- [Name](#Name)
|
||||
- [Value](#Value)
|
||||
- [Kind](#Kind)
|
||||
- [IsEmbedded](#IsEmbedded)
|
||||
- [IsExported](#IsExported)
|
||||
- [IsZero](#IsZero)
|
||||
- [IsSlice](#IsSlice)
|
||||
|
||||
> 注意:由于`Field`继承于`Struct`,所以同样拥有`Struct`所有方法,如下:
|
||||
|
||||
- [ToMap](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN.md#ToMap)
|
||||
- [Fields](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN.md#Fields)
|
||||
- [Field](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN.md#Field)
|
||||
- [IsStruct](https://github.com/duke-git/lancet/blob/main/docs/structs/struct_zh-CN.md#IsStruct)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## API 文档:
|
||||
|
||||
### <span id="Tag">Tag</span>
|
||||
|
||||
<p>获取`Field`的`Tag`,默认的tag key是json</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) Tag() *Tag
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
p1 := &Parent{"111"}
|
||||
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
tag := n.Tag()
|
||||
|
||||
fmt.Println(tag.Name)
|
||||
|
||||
// Output:
|
||||
// name
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Value">Value</span>
|
||||
|
||||
<p>获取`Field`属性的值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) Value() any
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
p1 := &Parent{"111"}
|
||||
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
|
||||
fmt.Println(n.Value())
|
||||
|
||||
// Output:
|
||||
// 111
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsEmbedded">IsEmbedded</span>
|
||||
|
||||
<p>判断属性是否为嵌入</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) IsEmbedded() bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
}
|
||||
type Child struct {
|
||||
Parent
|
||||
Age int
|
||||
}
|
||||
c1 := &Child{}
|
||||
c1.Name = "111"
|
||||
c1.Age = 11
|
||||
|
||||
s := structs.New(c1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
|
||||
fmt.Println(n.IsEmbedded())
|
||||
fmt.Println(a.IsEmbedded())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsExported">IsExported</span>
|
||||
|
||||
<p>判断属性是否导出</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) IsExported() bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
age int
|
||||
}
|
||||
p1 := &Parent{Name: "11", age: 11}
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("age")
|
||||
|
||||
fmt.Println(n.IsExported())
|
||||
fmt.Println(a.IsExported())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsZero">IsZero</span>
|
||||
|
||||
<p>判断属性是否为零值</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) IsZero() bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p1 := &Parent{Age: 11}
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
|
||||
fmt.Println(n.IsZero())
|
||||
fmt.Println(a.IsZero())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Name">Name</span>
|
||||
|
||||
<p>获取属性名</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) Name() string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p1 := &Parent{Age: 11}
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
|
||||
fmt.Println(n.Name())
|
||||
fmt.Println(a.Name())
|
||||
|
||||
// Output:
|
||||
// Name
|
||||
// Age
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Kind">Kind</span>
|
||||
|
||||
<p>获取属性Kind</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) Kind() reflect.Kind
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p1 := &Parent{Age: 11}
|
||||
s := structs.New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
|
||||
fmt.Println(n.Kind())
|
||||
fmt.Println(a.Kind())
|
||||
|
||||
// Output:
|
||||
// string
|
||||
// int
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsSlice">IsSlice</span>
|
||||
|
||||
<p>判断属性是否是切片</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (f *Field) IsSlice() bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Parent struct {
|
||||
Name string
|
||||
arr []int
|
||||
}
|
||||
|
||||
p1 := &Parent{arr: []int{1, 2, 3}}
|
||||
s := structs.New(p1)
|
||||
a, _ := s.Field("arr")
|
||||
|
||||
fmt.Println(a.IsSlice())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
```
|
||||
213
docs/structs/struct.md
Normal file
213
docs/structs/struct.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# Structs
|
||||
|
||||
Struct is abstract struct for provide several high level functions
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/structs/struct.go](https://github.com/duke-git/lancet/blob/main/structs/struct.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index:
|
||||
- [New](#New)
|
||||
- [ToMap](#ToMap)
|
||||
- [Fields](#Fields)
|
||||
- [Field](#Field)
|
||||
- [IsStruct](#IsStruct)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation:
|
||||
|
||||
### <span id="New">New</span>
|
||||
|
||||
<p>The constructor function of the `Struct` </p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func New(value any, tagName ...string) *Struct
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
p1 := &People{Name: "11"}
|
||||
s := structs.New(p1)
|
||||
// to do something
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToMap">ToMap</span>
|
||||
|
||||
<p>convert a valid struct to a map</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s *Struct) ToMap() (map[string]any, error)
|
||||
```
|
||||
|
||||
> In addition, provided a convenient static function ToMap
|
||||
|
||||
```go
|
||||
func ToMap(v any) (map[string]any, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
p1 := &People{Name: "11"}
|
||||
// use constructor function
|
||||
s1 := structs.New(p1)
|
||||
m1, _ := s1.ToMap()
|
||||
|
||||
fmt.Println(m1)
|
||||
|
||||
// use static function
|
||||
m2, _ := structs.ToMap(p1)
|
||||
|
||||
fmt.Println(m2)
|
||||
|
||||
// Output:
|
||||
// map[name:11]
|
||||
// map[name:11]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Fields">Fields</span>
|
||||
|
||||
<p>Get all fields of a given struct, that the fields are abstract struct field</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s *Struct) Fields() []*Field
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
p1 := &People{Name: "11"}
|
||||
s := structs.New(p1)
|
||||
fields := s.Fields()
|
||||
|
||||
fmt.Println(len(fields))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Field">Field</span>
|
||||
|
||||
<p>Get an abstract field of a struct by given field name </p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s *Struct) Field(name string) *Field
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
p1 := &People{Name: "11"}
|
||||
s := structs.New(p1)
|
||||
f := s.Field("Name")
|
||||
|
||||
fmt.Println(f.Value())
|
||||
|
||||
// Output:
|
||||
// 11
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsStruct">IsStruct</span>
|
||||
|
||||
<p>Check if the struct is valid</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s *Struct) IsStruct() bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
p1 := &People{Name: "11"}
|
||||
s := structs.New(p1)
|
||||
|
||||
fmt.Println(s.IsStruct())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
```
|
||||
212
docs/structs/struct_zh-CN.md
Normal file
212
docs/structs/struct_zh-CN.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# Structs
|
||||
|
||||
structs 包封装了一个抽象的`Struct`结构体,提供了操作`struct`的相关函数
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/structs/struct.go](https://github.com/duke-git/lancet/blob/main/structs/struct.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 用法:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
```
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 目录:
|
||||
- [New](#New)
|
||||
- [ToMap](#ToMap)
|
||||
- [Fields](#Fields)
|
||||
- [Field](#Field)
|
||||
- [IsStruct](#IsStruct)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## API 文档:
|
||||
|
||||
### <span id="New">New</span>
|
||||
|
||||
<p>`Struct`结构体的构造函数</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func New(value any, tagName ...string) *Struct
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
p1 := &People{Name: "11"}
|
||||
s := structs.New(p1)
|
||||
// to do something
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToMap">ToMap</span>
|
||||
|
||||
<p>将一个合法的struct对象转换为map[string]any</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *Struct) ToMap() (map[string]any, error)
|
||||
```
|
||||
|
||||
<b>除此之外,提供一个便捷的静态方法ToMap</b>
|
||||
|
||||
```go
|
||||
func ToMap(v any) (map[string]any, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
p1 := &People{Name: "11"}
|
||||
s1 := structs.New(p1)
|
||||
m1, _ := s1.ToMap()
|
||||
|
||||
fmt.Println(m1)
|
||||
|
||||
// 如果不需要Struct更多的方法,可以直接使用ToMap
|
||||
m2, _ := structs.ToMap(p1)
|
||||
|
||||
fmt.Println(m2)
|
||||
|
||||
// Output:
|
||||
// map[name:11]
|
||||
// map[name:11]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Fields">Fields</span>
|
||||
|
||||
<p>获取一个struct对象的属性列表</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *Struct) Fields() []*Field
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
p1 := &People{Name: "11"}
|
||||
s := structs.New(p1)
|
||||
fields := s.Fields()
|
||||
|
||||
fmt.Println(len(fields))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Field">Field</span>
|
||||
|
||||
<p>根据属性名获取一个struct对象的属性</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *Struct) Field(name string) *Field
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
p1 := &People{Name: "11"}
|
||||
s := structs.New(p1)
|
||||
f := s.Field("Name")
|
||||
|
||||
fmt.Println(f.Value())
|
||||
|
||||
// Output:
|
||||
// 11
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsStruct">IsStruct</span>
|
||||
|
||||
<p>判断是否为一个合法的struct对象</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s *Struct) IsStruct() bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
p1 := &People{Name: "11"}
|
||||
s := structs.New(p1)
|
||||
|
||||
fmt.Println(s.IsStruct())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
```
|
||||
@@ -45,6 +45,7 @@ import (
|
||||
- [Unwrap](#Unwrap)
|
||||
- [SplitWords](#SplitWords)
|
||||
- [WordCount](#WordCount)
|
||||
- [RemoveNonPrintable](#RemoveNonPrintable)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -937,4 +938,35 @@ func main() {
|
||||
// 0
|
||||
// 0
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="RemoveNonPrintable">RemoveNonPrintable</span>
|
||||
|
||||
<p>Remove non-printable characters from a string.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RemoveNonPrintable(str string) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := strutil.RemoveNonPrintable("hello\u00a0 \u200bworld\n")
|
||||
result2 := strutil.RemoveNonPrintable("你好😄")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
// Output:
|
||||
// hello world
|
||||
// 你好😄
|
||||
}
|
||||
```
|
||||
@@ -45,6 +45,7 @@ import (
|
||||
- [Unwrap](#Unwrap)
|
||||
- [SplitWords](#SplitWords)
|
||||
- [WordCount](#WordCount)
|
||||
- [RemoveNonPrintable](#RemoveNonPrintable)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -936,4 +937,35 @@ func main() {
|
||||
// 0
|
||||
// 0
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="RemoveNonPrintable">RemoveNonPrintable</span>
|
||||
|
||||
<p>删除字符串中不可打印的字符。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RemoveNonPrintable(str string) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := strutil.RemoveNonPrintable("hello\u00a0 \u200bworld\n")
|
||||
result2 := strutil.RemoveNonPrintable("你好😄")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
// Output:
|
||||
// hello world
|
||||
// 你好😄
|
||||
}
|
||||
```
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
- [IsAlpha](#IsAlpha)
|
||||
- [IsAllUpper](#IsAllUpper)
|
||||
- [IsAllLower](#IsAllLower)
|
||||
- [IsASCII](#IsASCII)
|
||||
- [IsBase64](#IsBase64)
|
||||
- [IsChineseMobile](#IsChineseMobile)
|
||||
- [IsChineseIdNum](#IsChineseIdNum)
|
||||
@@ -50,6 +51,7 @@ import (
|
||||
- [IsWeakPassword](#IsWeakPassword)
|
||||
- [IsZeroValue](#IsZeroValue)
|
||||
- [IsGBK](#IsGBK)
|
||||
- [IsPrintable](#IsPrintable)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -293,6 +295,46 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsASCII">IsASCII</span>
|
||||
|
||||
<p>Checks if string is all ASCII char.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsASCII(str string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := validator.IsASCII("ABC")
|
||||
result2 := validator.IsASCII("123")
|
||||
result3 := validator.IsASCII("")
|
||||
result4 := validator.IsASCII("😄")
|
||||
result5 := validator.IsASCII("你好")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsBase64">IsBase64</span>
|
||||
|
||||
<p>Check if the string is base64 string.</p>
|
||||
@@ -990,3 +1032,44 @@ func main() {
|
||||
// true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="IsPrintable">IsPrintable</span>
|
||||
|
||||
<p>Checks if string is all printable chars.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsPrintable(str string) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := validator.IsPrintable("ABC")
|
||||
result2 := validator.IsPrintable("{id: 123}")
|
||||
result3 := validator.IsPrintable("")
|
||||
result4 := validator.IsPrintable("😄")
|
||||
result5 := validator.IsPrintable("\u0000")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
- [IsAlpha](#IsAlpha)
|
||||
- [IsAllUpper](#IsAllUpper)
|
||||
- [IsAllLower](#IsAllLower)
|
||||
- [IsASCII](#IsASCII)
|
||||
- [IsBase64](#IsBase64)
|
||||
- [IsChineseMobile](#IsChineseMobile)
|
||||
- [IsChineseIdNum](#IsChineseIdNum)
|
||||
@@ -50,6 +51,7 @@ import (
|
||||
- [IsWeakPassword](#IsWeakPassword)
|
||||
- [IsZeroValue](#IsZeroValue)
|
||||
- [IsGBK](#IsGBK)
|
||||
- [IsPrintable](#IsPrintable)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -293,6 +295,46 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsASCII">IsASCII</span>
|
||||
|
||||
<p>验证字符串全部为ASCII字符。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsASCII(str string) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := validator.IsASCII("ABC")
|
||||
result2 := validator.IsASCII("123")
|
||||
result3 := validator.IsASCII("")
|
||||
result4 := validator.IsASCII("😄")
|
||||
result5 := validator.IsASCII("你好")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsBase64">IsBase64</span>
|
||||
|
||||
<p>验证字符串是否是base64编码</p>
|
||||
@@ -990,3 +1032,44 @@ func main() {
|
||||
// true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="IsPrintable">IsPrintable</span>
|
||||
|
||||
<p>检查字符串是否全部为可打印字符。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsPrintable(str string) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := validator.IsPrintable("ABC")
|
||||
result2 := validator.IsPrintable("{id: 123}")
|
||||
result3 := validator.IsPrintable("")
|
||||
result4 := validator.IsPrintable("😄")
|
||||
result5 := validator.IsPrintable("\u0000")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
@@ -79,16 +79,18 @@ func (a *Assert) LessOrEqual(expected, actual any) {
|
||||
}
|
||||
|
||||
// IsNil check if value is nil
|
||||
func (a *Assert) IsNil(value any) {
|
||||
if value != nil {
|
||||
makeTestFailed(a.T, a.CaseName, nil, value)
|
||||
func (a *Assert) IsNil(v any) {
|
||||
if v == nil || (reflect.ValueOf(v).Kind() == reflect.Ptr && reflect.ValueOf(v).IsNil()) {
|
||||
return
|
||||
}
|
||||
|
||||
makeTestFailed(a.T, a.CaseName, nil, v)
|
||||
}
|
||||
|
||||
// IsNotNil check if value is not nil
|
||||
func (a *Assert) IsNotNil(value any) {
|
||||
if value == nil {
|
||||
makeTestFailed(a.T, a.CaseName, "not nil", value)
|
||||
func (a *Assert) IsNotNil(v any) {
|
||||
if v == nil || (reflect.ValueOf(v).Kind() == reflect.Ptr && reflect.ValueOf(v).IsNil()) {
|
||||
makeTestFailed(a.T, a.CaseName, "not nil", v)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
51
internal/error_join.go
Normal file
51
internal/error_join.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package internal
|
||||
|
||||
// Note: this file is copyed from the go standart repo (https://github.com/golang/go/blob/master/src/errors/join.go).
|
||||
// just in order to adapt under go1.9
|
||||
// do not use it outside lancet lib.
|
||||
|
||||
// Join returns an error that wraps the given errors.
|
||||
// Any nil error values are discarded.
|
||||
// Join returns nil if errs contains no non-nil values.
|
||||
// The error formats as the concatenation of the strings obtained
|
||||
// by calling the Error method of each element of errs, with a newline
|
||||
// between each string.
|
||||
func JoinError(errs ...error) error {
|
||||
n := 0
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
n++
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
e := &joinError{
|
||||
errs: make([]error, 0, n),
|
||||
}
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
e.errs = append(e.errs, err)
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
type joinError struct {
|
||||
errs []error
|
||||
}
|
||||
|
||||
func (e *joinError) Error() string {
|
||||
var b []byte
|
||||
for i, err := range e.errs {
|
||||
if i > 0 {
|
||||
b = append(b, '\n')
|
||||
}
|
||||
b = append(b, err.Error()...)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (e *joinError) Unwrap() []error {
|
||||
return e.errs
|
||||
}
|
||||
@@ -183,3 +183,76 @@ func Average[T constraints.Integer | constraints.Float](numbers ...T) T {
|
||||
}
|
||||
return sum / n
|
||||
}
|
||||
|
||||
// Range creates a slice of numbers from start with specified count, element step is 1.
|
||||
// Play: https://go.dev/play/p/9ke2opxa8ZP
|
||||
func Range[T constraints.Integer | constraints.Float](start T, count int) []T {
|
||||
size := count
|
||||
if count < 0 {
|
||||
size = -count
|
||||
}
|
||||
|
||||
result := make([]T, size)
|
||||
|
||||
for i, j := 0, start; i < size; i, j = i+1, j+1 {
|
||||
result[i] = j
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// RangeWithStep creates a slice of numbers from start to end with specified step.
|
||||
// Play: https://go.dev/play/p/akLWz0EqOSM
|
||||
func RangeWithStep[T constraints.Integer | constraints.Float](start, end, step T) []T {
|
||||
result := []T{}
|
||||
|
||||
if start >= end || step == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
for i := start; i < end; i += step {
|
||||
result = append(result, i)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// AngleToRadian converts angle value to radian value.
|
||||
// Play: todo
|
||||
func AngleToRadian(angle float64) float64 {
|
||||
radian := angle * (math.Pi / 180)
|
||||
return radian
|
||||
}
|
||||
|
||||
// RadianToAngle converts radian value to angle value.
|
||||
// Play: todo
|
||||
func RadianToAngle(radian float64) float64 {
|
||||
angle := radian * (180 / math.Pi)
|
||||
return angle
|
||||
}
|
||||
|
||||
// PointDistance get two points distance.
|
||||
// Play: todo
|
||||
func PointDistance(x1, y1, x2, y2 float64) float64 {
|
||||
a := x1 - x2
|
||||
b := y1 - y2
|
||||
c := math.Pow(a, 2) + math.Pow(b, 2)
|
||||
|
||||
return math.Sqrt(c)
|
||||
}
|
||||
|
||||
// IsPrimes checks if number is prime number.
|
||||
// Play: todo
|
||||
func IsPrime(n int) bool {
|
||||
if n < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 2; i <= int(math.Sqrt(float64(n))); i++ {
|
||||
if n%i == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package mathutil
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
func ExampleExponent() {
|
||||
result1 := Exponent(10, 0)
|
||||
@@ -185,3 +188,96 @@ func ExampleMinBy() {
|
||||
// ab
|
||||
//
|
||||
}
|
||||
|
||||
func ExampleRange() {
|
||||
result1 := Range(1, 4)
|
||||
result2 := Range(1, -4)
|
||||
result3 := Range(-4, 4)
|
||||
result4 := Range(1.0, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4]
|
||||
// [1 2 3 4]
|
||||
// [-4 -3 -2 -1]
|
||||
// [1 2 3 4]
|
||||
}
|
||||
|
||||
func ExampleRangeWithStep() {
|
||||
result1 := RangeWithStep(1, 4, 1)
|
||||
result2 := RangeWithStep(1, -1, 0)
|
||||
result3 := RangeWithStep(-4, 1, 2)
|
||||
result4 := RangeWithStep(1.0, 4.0, 1.1)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [1 2 3]
|
||||
// []
|
||||
// [-4 -2 0]
|
||||
// [1 2.1 3.2]
|
||||
}
|
||||
|
||||
func ExampleAngleToRadian() {
|
||||
result1 := AngleToRadian(45)
|
||||
result2 := AngleToRadian(90)
|
||||
result3 := AngleToRadian(180)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 0.7853981633974483
|
||||
// 1.5707963267948966
|
||||
// 3.141592653589793
|
||||
}
|
||||
|
||||
func ExampleRadianToAngle() {
|
||||
result1 := RadianToAngle(math.Pi)
|
||||
result2 := RadianToAngle(math.Pi / 2)
|
||||
result3 := RadianToAngle(math.Pi / 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 180
|
||||
// 90
|
||||
// 45
|
||||
}
|
||||
|
||||
func ExamplePointDistance() {
|
||||
result1 := PointDistance(1, 1, 4, 5)
|
||||
|
||||
fmt.Println(result1)
|
||||
|
||||
// Output:
|
||||
// 5
|
||||
}
|
||||
|
||||
func ExampleIsPrime() {
|
||||
result1 := IsPrime(-1)
|
||||
result2 := IsPrime(0)
|
||||
result3 := IsPrime(1)
|
||||
result4 := IsPrime(2)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mathutil
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
@@ -137,3 +138,76 @@ func TestMinBy(t *testing.T) {
|
||||
})
|
||||
assert.Equal("", res3)
|
||||
}
|
||||
|
||||
func TestRange(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRange")
|
||||
|
||||
result1 := Range(1, 4)
|
||||
result2 := Range(1, -4)
|
||||
result3 := Range(-4, 4)
|
||||
result4 := Range(1.0, 4)
|
||||
result5 := Range(1, 0)
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4}, result1)
|
||||
assert.Equal([]int{1, 2, 3, 4}, result2)
|
||||
assert.Equal([]int{-4, -3, -2, -1}, result3)
|
||||
assert.Equal([]float64{1.0, 2.0, 3.0, 4.0}, result4)
|
||||
assert.Equal([]int{}, result5)
|
||||
}
|
||||
|
||||
func TestRangeWithStep(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRangeWithStep")
|
||||
|
||||
result1 := RangeWithStep(1, 4, 1)
|
||||
result2 := RangeWithStep(1, -1, 0)
|
||||
result3 := RangeWithStep(-4, 1, 2)
|
||||
result4 := RangeWithStep(1.0, 4.0, 1.1)
|
||||
|
||||
assert.Equal([]int{1, 2, 3}, result1)
|
||||
assert.Equal([]int{}, result2)
|
||||
assert.Equal([]int{-4, -2, 0}, result3)
|
||||
assert.Equal([]float64{1.0, 2.1, 3.2}, result4)
|
||||
}
|
||||
|
||||
func TestAngleToRadian(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestAngleToRadian")
|
||||
|
||||
result1 := AngleToRadian(45)
|
||||
result2 := AngleToRadian(90)
|
||||
result3 := AngleToRadian(180)
|
||||
|
||||
assert.Equal(0.7853981633974483, result1)
|
||||
assert.Equal(1.5707963267948966, result2)
|
||||
assert.Equal(3.141592653589793, result3)
|
||||
}
|
||||
|
||||
func TestRadianToAngle(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestAngleToRadian")
|
||||
|
||||
result1 := RadianToAngle(math.Pi)
|
||||
result2 := RadianToAngle(math.Pi / 2)
|
||||
result3 := RadianToAngle(math.Pi / 4)
|
||||
|
||||
assert.Equal(float64(180), result1)
|
||||
assert.Equal(float64(90), result2)
|
||||
assert.Equal(float64(45), result3)
|
||||
}
|
||||
|
||||
func TestPointDistance(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPointDistance")
|
||||
|
||||
result1 := PointDistance(1, 1, 4, 5)
|
||||
|
||||
assert.Equal(float64(5), result1)
|
||||
}
|
||||
|
||||
func TestIsPrime(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsPrime")
|
||||
|
||||
assert.Equal(false, IsPrime(-1))
|
||||
assert.Equal(false, IsPrime(0))
|
||||
assert.Equal(false, IsPrime(1))
|
||||
assert.Equal(true, IsPrime(2))
|
||||
assert.Equal(true, IsPrime(3))
|
||||
assert.Equal(false, IsPrime(4))
|
||||
}
|
||||
|
||||
@@ -21,12 +21,11 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
@@ -219,7 +218,7 @@ func (client *HttpClient) setTLS(rawUrl string) {
|
||||
}
|
||||
}
|
||||
|
||||
// setHeader set http rquest header
|
||||
// setHeader set http request header
|
||||
func (client *HttpClient) setHeader(req *http.Request, headers http.Header) {
|
||||
if headers == nil {
|
||||
headers = make(http.Header)
|
||||
@@ -278,29 +277,15 @@ func validateRequest(req *HttpRequest) error {
|
||||
// StructToUrlValues convert struct to url valuse,
|
||||
// only convert the field which is exported and has `json` tag.
|
||||
// Play: https://go.dev/play/p/pFqMkM40w9z
|
||||
func StructToUrlValues(targetStruct any) url.Values {
|
||||
rv := reflect.ValueOf(targetStruct)
|
||||
rt := reflect.TypeOf(targetStruct)
|
||||
|
||||
if rt.Kind() == reflect.Ptr {
|
||||
rt = rt.Elem()
|
||||
}
|
||||
if rt.Kind() != reflect.Struct {
|
||||
panic(fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", targetStruct))
|
||||
}
|
||||
|
||||
func StructToUrlValues(targetStruct any) (url.Values, error) {
|
||||
result := url.Values{}
|
||||
|
||||
fieldNum := rt.NumField()
|
||||
pattern := `^[A-Z]`
|
||||
regex := regexp.MustCompile(pattern)
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
name := rt.Field(i).Name
|
||||
tag := rt.Field(i).Tag.Get("json")
|
||||
if regex.MatchString(name) && tag != "" {
|
||||
result.Add(tag, fmt.Sprintf("%v", rv.Field(i).Interface()))
|
||||
}
|
||||
s, err := convertor.StructToMap(targetStruct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range s {
|
||||
result.Add(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
|
||||
return result
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -217,30 +217,31 @@ func TestStructToUrlValues(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStructToUrlValues")
|
||||
|
||||
type TodoQuery struct {
|
||||
Id int `json:"id"`
|
||||
UserId int `json:"userId"`
|
||||
Id int `json:"id"`
|
||||
UserId int `json:"userId"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
todoQuery := TodoQuery{
|
||||
item1 := TodoQuery{
|
||||
Id: 1,
|
||||
UserId: 1,
|
||||
UserId: 123,
|
||||
Name: "",
|
||||
}
|
||||
todoValues, err := StructToUrlValues(item1)
|
||||
if err != nil {
|
||||
t.Errorf("params is invalid: %v", err)
|
||||
}
|
||||
todoValues := StructToUrlValues(todoQuery)
|
||||
|
||||
assert.Equal("1", todoValues.Get("id"))
|
||||
assert.Equal("1", todoValues.Get("userId"))
|
||||
assert.Equal("123", todoValues.Get("userId"))
|
||||
assert.Equal("", todoValues.Get("name"))
|
||||
|
||||
request := &HttpRequest{
|
||||
RawURL: "https://jsonplaceholder.typicode.com/todos",
|
||||
Method: "GET",
|
||||
QueryParams: todoValues,
|
||||
item2 := TodoQuery{
|
||||
Id: 2,
|
||||
UserId: 456,
|
||||
}
|
||||
queryValues2, _ := StructToUrlValues(item2)
|
||||
|
||||
httpClient := NewHttpClient()
|
||||
resp, err := httpClient.SendRequest(request)
|
||||
if err != nil || resp.StatusCode != 200 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Log("response: ", string(body))
|
||||
assert.Equal("2", queryValues2.Get("id"))
|
||||
assert.Equal("456", queryValues2.Get("userId"))
|
||||
assert.Equal("", queryValues2.Get("name"))
|
||||
}
|
||||
|
||||
@@ -123,21 +123,45 @@ func ExampleHttpClient_DecodeResponse() {
|
||||
|
||||
func ExampleStructToUrlValues() {
|
||||
type TodoQuery struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Id int `json:"id"`
|
||||
UserId int `json:"userId"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Status string
|
||||
}
|
||||
todoQuery := TodoQuery{
|
||||
Id: 1,
|
||||
Name: "Test",
|
||||
item1 := TodoQuery{
|
||||
Id: 1,
|
||||
UserId: 123,
|
||||
Name: "test",
|
||||
Status: "completed",
|
||||
}
|
||||
queryValues1, err := StructToUrlValues(item1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
todoValues := StructToUrlValues(todoQuery)
|
||||
|
||||
fmt.Println(todoValues.Get("id"))
|
||||
fmt.Println(todoValues.Get("name"))
|
||||
item2 := TodoQuery{
|
||||
Id: 2,
|
||||
UserId: 456,
|
||||
}
|
||||
queryValues2, _ := StructToUrlValues(item2)
|
||||
|
||||
fmt.Println(queryValues1.Get("id"))
|
||||
fmt.Println(queryValues1.Get("userId"))
|
||||
fmt.Println(queryValues1.Get("name"))
|
||||
fmt.Println(queryValues1.Get("status"))
|
||||
|
||||
fmt.Println(queryValues2.Get("id"))
|
||||
fmt.Println(queryValues2.Get("userId"))
|
||||
fmt.Println(queryValues2.Get("name"))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// Test
|
||||
// 123
|
||||
// test
|
||||
//
|
||||
// 2
|
||||
// 456
|
||||
//
|
||||
}
|
||||
|
||||
func ExampleConvertMapToQueryString() {
|
||||
|
||||
14
pointer/pointer.go
Normal file
14
pointer/pointer.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package pointer
|
||||
|
||||
import "reflect"
|
||||
|
||||
// ExtractPointer returns the underlying value by the given interface type
|
||||
func ExtractPointer(value any) any {
|
||||
t := reflect.TypeOf(value)
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
if t.Kind() != reflect.Pointer {
|
||||
return value
|
||||
}
|
||||
return ExtractPointer(v.Elem().Interface())
|
||||
}
|
||||
18
pointer/pointer_test.go
Normal file
18
pointer/pointer_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package pointer
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExtractPointer(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestExtractPointer")
|
||||
|
||||
a := 1
|
||||
b := &a
|
||||
c := &b
|
||||
d := &c
|
||||
|
||||
assert.Equal(1, ExtractPointer(d))
|
||||
}
|
||||
279
promise/promise.go
Normal file
279
promise/promise.go
Normal file
@@ -0,0 +1,279 @@
|
||||
// Copyright 2023 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package promise contains some functions to support async programming.
|
||||
package promise
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
// Promise represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
|
||||
// ref : chebyrash/promise (https://github.com/chebyrash/promise)
|
||||
// see js promise: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
|
||||
type Promise[T any] struct {
|
||||
runnable func(resolve func(T), reject func(error))
|
||||
result T
|
||||
err error
|
||||
|
||||
pending bool
|
||||
|
||||
mu *sync.Mutex
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
// New create a new promise instance.
|
||||
func New[T any](runnable func(resolve func(T), reject func(error))) *Promise[T] {
|
||||
if runnable == nil {
|
||||
panic("runnable function should not be nil")
|
||||
}
|
||||
|
||||
p := &Promise[T]{
|
||||
runnable: runnable,
|
||||
pending: true,
|
||||
mu: &sync.Mutex{},
|
||||
wg: &sync.WaitGroup{},
|
||||
}
|
||||
|
||||
defer p.run()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Promise[T]) run() {
|
||||
p.wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if !p.pending {
|
||||
return
|
||||
}
|
||||
|
||||
if err := recover(); err != nil {
|
||||
p.reject(errors.New(fmt.Sprint(err)))
|
||||
}
|
||||
}()
|
||||
|
||||
p.runnable(p.resolve, p.reject)
|
||||
}()
|
||||
}
|
||||
|
||||
// Resolve returns a Promise that has been resolved with a given value.
|
||||
func Resolve[T any](resolution T) *Promise[T] {
|
||||
return &Promise[T]{
|
||||
result: resolution,
|
||||
pending: false,
|
||||
mu: &sync.Mutex{},
|
||||
wg: &sync.WaitGroup{},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Promise[T]) resolve(value T) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if !p.pending {
|
||||
return
|
||||
}
|
||||
|
||||
p.result = value
|
||||
p.pending = false
|
||||
|
||||
p.wg.Done()
|
||||
}
|
||||
|
||||
// Reject returns a Promise that has been rejected with a given error.
|
||||
func Reject[T any](err error) *Promise[T] {
|
||||
return &Promise[T]{
|
||||
err: err,
|
||||
pending: false,
|
||||
mu: &sync.Mutex{},
|
||||
wg: &sync.WaitGroup{},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Promise[T]) reject(err error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if !p.pending {
|
||||
return
|
||||
}
|
||||
|
||||
p.err = err
|
||||
p.pending = false
|
||||
|
||||
p.wg.Done()
|
||||
}
|
||||
|
||||
// Then allows chain calls to other promise methods.
|
||||
func Then[T1, T2 any](promise *Promise[T1], resolve1 func(value T1) T2) *Promise[T2] {
|
||||
return New(func(resolve2 func(T2), reject func(error)) {
|
||||
result, err := promise.Await()
|
||||
if err != nil {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
resolve2(resolve1(result))
|
||||
})
|
||||
}
|
||||
|
||||
// Then allows chain calls to other promise methods.
|
||||
func (p *Promise[T]) Then(resolve func(value T) T) *Promise[T] {
|
||||
return New(func(_resolve func(T), reject func(error)) {
|
||||
result, err := p.Await()
|
||||
if err != nil {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
_resolve(resolve(result))
|
||||
})
|
||||
}
|
||||
|
||||
// Catch allows to chain promises.
|
||||
func Catch[T any](promise *Promise[T], rejection func(err error) error) *Promise[T] {
|
||||
return New(func(resolve func(T), reject func(error)) {
|
||||
result, err := promise.Await()
|
||||
if err != nil {
|
||||
reject(rejection(err))
|
||||
return
|
||||
}
|
||||
resolve(result)
|
||||
})
|
||||
}
|
||||
|
||||
// Catch chain an existing promise with an intermediate reject function.
|
||||
func (p *Promise[T]) Catch(reject func(error) error) *Promise[T] {
|
||||
return New(func(resolve func(T), rej func(error)) {
|
||||
resutl, err := p.Await()
|
||||
if err != nil {
|
||||
rej(reject(err))
|
||||
return
|
||||
}
|
||||
resolve(resutl)
|
||||
})
|
||||
}
|
||||
|
||||
// Await blocks until the 'runable' to finish execution.
|
||||
func (p *Promise[T]) Await() (T, error) {
|
||||
p.wg.Wait()
|
||||
return p.result, p.err
|
||||
}
|
||||
|
||||
type tuple[T1, T2 any] struct {
|
||||
_1 T1
|
||||
_2 T2
|
||||
}
|
||||
|
||||
// All resolves when all of the promises have resolved, reject immediately upon any of the input promises rejecting.
|
||||
func All[T any](promises []*Promise[T]) *Promise[[]T] {
|
||||
if len(promises) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return New(func(resolve func([]T), reject func(error)) {
|
||||
valsChan := make(chan tuple[T, int], len(promises))
|
||||
errsChan := make(chan error, 1)
|
||||
|
||||
for idx, p := range promises {
|
||||
idx := idx
|
||||
_ = Then(p, func(data T) T {
|
||||
valsChan <- tuple[T, int]{_1: data, _2: idx}
|
||||
return data
|
||||
})
|
||||
_ = Catch(p, func(err error) error {
|
||||
errsChan <- err
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
resolutions := make([]T, len(promises))
|
||||
for idx := 0; idx < len(promises); idx++ {
|
||||
select {
|
||||
case val := <-valsChan:
|
||||
resolutions[val._2] = val._1
|
||||
case err := <-errsChan:
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
resolve(resolutions)
|
||||
})
|
||||
}
|
||||
|
||||
// Race will settle the first fullfiled promise among muti promises.
|
||||
func Race[T any](promises []*Promise[T]) *Promise[T] {
|
||||
if len(promises) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return New(func(resolve func(T), reject func(error)) {
|
||||
valsChan := make(chan T, 1)
|
||||
errsChan := make(chan error, 1)
|
||||
|
||||
for _, p := range promises {
|
||||
_ = Then(p, func(data T) T {
|
||||
valsChan <- data
|
||||
return data
|
||||
})
|
||||
_ = Catch(p, func(err error) error {
|
||||
errsChan <- err
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
select {
|
||||
case val := <-valsChan:
|
||||
resolve(val)
|
||||
case err := <-errsChan:
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Any resolves as soon as any of the input's Promises resolve, with the value of the resolved Promise.
|
||||
// Any rejects if all of the given Promises are rejected with a combination of all errors.
|
||||
func Any[T any](promises []*Promise[T]) *Promise[T] {
|
||||
if len(promises) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return New(func(resolve func(T), reject func(error)) {
|
||||
valsChan := make(chan T, 1)
|
||||
errsChan := make(chan tuple[error, int], len(promises))
|
||||
|
||||
for idx, p := range promises {
|
||||
idx := idx
|
||||
_ = Then(p, func(data T) T {
|
||||
valsChan <- data
|
||||
return data
|
||||
})
|
||||
_ = Catch(p, func(err error) error {
|
||||
errsChan <- tuple[error, int]{_1: err, _2: idx}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
errs := make([]error, len(promises))
|
||||
for idx := 0; idx < len(promises); idx++ {
|
||||
select {
|
||||
case val := <-valsChan:
|
||||
resolve(val)
|
||||
return
|
||||
case err := <-errsChan:
|
||||
errs[err._2] = err._1
|
||||
}
|
||||
}
|
||||
|
||||
errCombo := errs[0]
|
||||
for _, err := range errs[1:] {
|
||||
errCombo = internal.JoinError(err)
|
||||
}
|
||||
|
||||
reject(errCombo)
|
||||
})
|
||||
}
|
||||
195
promise/promise_example_test.go
Normal file
195
promise/promise_example_test.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package promise
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
p := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("hello")
|
||||
})
|
||||
|
||||
val, err := p.Await()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(val)
|
||||
|
||||
// Output:
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleThen() {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("hello ")
|
||||
})
|
||||
|
||||
p2 := Then(p1, func(s string) string {
|
||||
return s + "world"
|
||||
})
|
||||
|
||||
result, err := p2.Await()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// hello world
|
||||
}
|
||||
|
||||
func ExamplePromise_Then() {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("hello ")
|
||||
})
|
||||
|
||||
p2 := p1.Then(func(s string) string {
|
||||
return s + "world"
|
||||
})
|
||||
|
||||
result, err := p2.Await()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// hello world
|
||||
}
|
||||
|
||||
func ExampleCatch() {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
err := errors.New("error1")
|
||||
reject(err)
|
||||
})
|
||||
|
||||
p2 := Catch(p1, func(err error) error {
|
||||
e := errors.New("error2")
|
||||
return internal.JoinError(err, e)
|
||||
})
|
||||
|
||||
_, err := p1.Await()
|
||||
|
||||
fmt.Println(err.Error())
|
||||
|
||||
result2, err := p2.Await()
|
||||
|
||||
fmt.Println(result2)
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
// error1
|
||||
//
|
||||
// error1
|
||||
// error2
|
||||
}
|
||||
|
||||
func ExamplePromise_Catch() {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
err := errors.New("error1")
|
||||
reject(err)
|
||||
})
|
||||
|
||||
p2 := p1.Catch(func(err error) error {
|
||||
e := errors.New("error2")
|
||||
return internal.JoinError(err, e)
|
||||
})
|
||||
|
||||
_, err := p1.Await()
|
||||
|
||||
fmt.Println(err.Error())
|
||||
|
||||
result2, err := p2.Await()
|
||||
|
||||
fmt.Println(result2)
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
// error1
|
||||
//
|
||||
// error1
|
||||
// error2
|
||||
}
|
||||
|
||||
func ExampleAll() {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("a")
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("b")
|
||||
})
|
||||
p3 := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("c")
|
||||
})
|
||||
|
||||
pms := []*Promise[string]{p1, p2, p3}
|
||||
p := All(pms)
|
||||
|
||||
result, err := p.Await()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
}
|
||||
|
||||
func ExampleAny() {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
time.Sleep(time.Millisecond * 250)
|
||||
resolve("fast")
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
resolve("slow")
|
||||
})
|
||||
p3 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error"))
|
||||
})
|
||||
|
||||
pms := []*Promise[string]{p1, p2, p3}
|
||||
p := Any(pms)
|
||||
|
||||
result, err := p.Await()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// fast
|
||||
}
|
||||
|
||||
func ExampleRace() {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
resolve("fast")
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
time.Sleep(time.Millisecond * 300)
|
||||
resolve("slow")
|
||||
})
|
||||
|
||||
pms := []*Promise[string]{p1, p2}
|
||||
p := Race(pms)
|
||||
|
||||
result, err := p.Await()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// fast
|
||||
}
|
||||
302
promise/promise_test.go
Normal file
302
promise/promise_test.go
Normal file
@@ -0,0 +1,302 @@
|
||||
package promise
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestResolve(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestResolve")
|
||||
|
||||
p := Resolve("abc")
|
||||
|
||||
assert.Equal("abc", p.result)
|
||||
assert.Equal(false, p.pending)
|
||||
}
|
||||
|
||||
func TestReject(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestReject")
|
||||
|
||||
err := errors.New("error")
|
||||
p := Reject[string](err)
|
||||
|
||||
assert.Equal("error", p.err.Error())
|
||||
assert.Equal(false, p.pending)
|
||||
}
|
||||
|
||||
func TestThen(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestThen")
|
||||
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("abc")
|
||||
})
|
||||
|
||||
p2 := Then(p1, func(data string) string {
|
||||
return data + "de"
|
||||
})
|
||||
|
||||
val, err := p1.Await()
|
||||
assert.IsNil(err)
|
||||
assert.Equal("abc", val)
|
||||
|
||||
val, err = p2.Await()
|
||||
assert.IsNil(err)
|
||||
assert.Equal("abcde", val)
|
||||
}
|
||||
|
||||
func TestPromise_Then(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPromise_Then")
|
||||
|
||||
p1 := New(func(resolve func(int), reject func(error)) {
|
||||
resolve(1)
|
||||
})
|
||||
|
||||
p2 := p1.Then(func(n int) int {
|
||||
return n + 2
|
||||
})
|
||||
|
||||
val, err := p1.Await()
|
||||
assert.IsNil(err)
|
||||
assert.Equal(1, val)
|
||||
|
||||
val, err = p2.Await()
|
||||
assert.IsNil(err)
|
||||
assert.Equal(3, val)
|
||||
}
|
||||
|
||||
func TestCatch(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCatch")
|
||||
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
err := errors.New("error1")
|
||||
reject(err)
|
||||
})
|
||||
|
||||
p2 := Catch(p1, func(err error) error {
|
||||
e := errors.New("error2")
|
||||
return internal.JoinError(err, e)
|
||||
})
|
||||
|
||||
val, err := p1.Await()
|
||||
assert.Equal("", val)
|
||||
assert.IsNotNil(err)
|
||||
assert.Equal("error1", err.Error())
|
||||
|
||||
val, err = p2.Await()
|
||||
|
||||
assert.Equal("", val)
|
||||
assert.IsNotNil(err)
|
||||
assert.Equal("error1\nerror2", err.Error())
|
||||
}
|
||||
|
||||
func TestPromise_Catch(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPromise_Catch")
|
||||
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
err := errors.New("error1")
|
||||
reject(err)
|
||||
})
|
||||
|
||||
p2 := p1.Catch(func(err error) error {
|
||||
e := errors.New("error2")
|
||||
return internal.JoinError(err, e)
|
||||
})
|
||||
|
||||
val, err := p1.Await()
|
||||
assert.Equal("", val)
|
||||
assert.IsNotNil(err)
|
||||
assert.Equal("error1", err.Error())
|
||||
|
||||
val, err = p2.Await()
|
||||
|
||||
assert.Equal("", val)
|
||||
assert.IsNotNil(err)
|
||||
assert.Equal("error1\nerror2", err.Error())
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPromise_All")
|
||||
|
||||
t.Run("AllPromisesFullfilled", func(_ *testing.T) {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("a")
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("b")
|
||||
})
|
||||
p3 := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("c")
|
||||
})
|
||||
|
||||
p := All([]*Promise[string]{p1, p2, p3})
|
||||
|
||||
val, err := p.Await()
|
||||
assert.Equal([]string{"a", "b", "c"}, val)
|
||||
assert.IsNil(err)
|
||||
})
|
||||
|
||||
t.Run("EmptyPromises", func(_ *testing.T) {
|
||||
var empty = []*Promise[any]{}
|
||||
p := All(empty)
|
||||
assert.IsNil(p)
|
||||
})
|
||||
|
||||
t.Run("PromisesContainRejected", func(_ *testing.T) {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
resolve("a")
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error1"))
|
||||
})
|
||||
p3 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error2"))
|
||||
})
|
||||
|
||||
p := All([]*Promise[string]{p1, p2, p3})
|
||||
|
||||
_, err := p.Await()
|
||||
|
||||
assert.IsNotNil(err)
|
||||
// assert.Equal("error1", err.Error())
|
||||
})
|
||||
|
||||
t.Run("PromisesOnlyRejected", func(_ *testing.T) {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error1"))
|
||||
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error2"))
|
||||
})
|
||||
p3 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error3"))
|
||||
})
|
||||
|
||||
p := All([]*Promise[string]{p1, p2, p3})
|
||||
|
||||
_, err := p.Await()
|
||||
|
||||
assert.IsNotNil(err)
|
||||
// assert.Equal("error1", err.Error())
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAny(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPromise_Any")
|
||||
|
||||
t.Run("AnyFullfilled", func(_ *testing.T) {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
time.Sleep(time.Millisecond * 250)
|
||||
resolve("fast")
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
resolve("slow")
|
||||
})
|
||||
p3 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error"))
|
||||
})
|
||||
|
||||
p := Any([]*Promise[string]{p1, p2, p3})
|
||||
|
||||
val, err := p.Await()
|
||||
assert.Equal("fast", val)
|
||||
assert.IsNil(err)
|
||||
})
|
||||
|
||||
t.Run("EmptyPromises", func(_ *testing.T) {
|
||||
var empty = []*Promise[any]{}
|
||||
p := Any(empty)
|
||||
assert.IsNil(p)
|
||||
})
|
||||
|
||||
t.Run("OnlyRejected", func(_ *testing.T) {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error1"))
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error2"))
|
||||
})
|
||||
|
||||
p := Any([]*Promise[string]{p1, p2})
|
||||
|
||||
_, err := p.Await()
|
||||
|
||||
assert.IsNotNil(err)
|
||||
// assert.Equal("error1", err.Error())
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestRace(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPromise_Race")
|
||||
|
||||
t.Run("PromisesFullfilled", func(_ *testing.T) {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
resolve("a")
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
time.Sleep(time.Millisecond * 300)
|
||||
resolve("b")
|
||||
})
|
||||
|
||||
p := Race([]*Promise[string]{p1, p2})
|
||||
|
||||
val, err := p.Await()
|
||||
assert.Equal("a", val)
|
||||
assert.IsNil(err)
|
||||
})
|
||||
|
||||
t.Run("EmptyPromises", func(_ *testing.T) {
|
||||
var empty = []*Promise[any]{}
|
||||
p := Race(empty)
|
||||
assert.IsNil(p)
|
||||
})
|
||||
|
||||
t.Run("PromisesContainRejected", func(_ *testing.T) {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
resolve("a")
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error1"))
|
||||
})
|
||||
p3 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error2"))
|
||||
})
|
||||
|
||||
p := Race([]*Promise[string]{p1, p2, p3})
|
||||
|
||||
val, err := p.Await()
|
||||
|
||||
assert.IsNotNil(err)
|
||||
// assert.Equal("error1", err.Error())
|
||||
assert.Equal("", val)
|
||||
})
|
||||
|
||||
t.Run("PromisesOnlyRejected", func(_ *testing.T) {
|
||||
p1 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error1"))
|
||||
|
||||
})
|
||||
p2 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error2"))
|
||||
})
|
||||
p3 := New(func(resolve func(string), reject func(error)) {
|
||||
reject(errors.New("error3"))
|
||||
})
|
||||
|
||||
p := Race([]*Promise[string]{p1, p2, p3})
|
||||
|
||||
_, err := p.Await()
|
||||
|
||||
assert.IsNotNil(err)
|
||||
// assert.Equal("error1", err.Error())
|
||||
})
|
||||
|
||||
}
|
||||
@@ -19,15 +19,11 @@ func ExampleContext() {
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := Retry(increaseNumber,
|
||||
Retry(increaseNumber,
|
||||
RetryDuration(time.Microsecond*50),
|
||||
Context(ctx),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(number)
|
||||
|
||||
// Output:
|
||||
|
||||
@@ -421,6 +421,17 @@ func ForEach[T any](slice []T, iteratee func(index int, item T)) {
|
||||
}
|
||||
}
|
||||
|
||||
// ForEachWithBreak iterates over elements of slice and invokes function for each element,
|
||||
// when iteratee return false, will break the for each loop.
|
||||
// Play: https://go.dev/play/p/qScs39f3D9W
|
||||
func ForEachWithBreak[T any](slice []T, iteratee func(index int, item T) bool) {
|
||||
for i, v := range slice {
|
||||
if !iteratee(i, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map creates an slice of values by running each element of slice thru iteratee function.
|
||||
// Play: https://go.dev/play/p/biaTefqPquw
|
||||
func Map[T any, U any](slice []T, iteratee func(index int, item T) U) []U {
|
||||
@@ -1083,7 +1094,7 @@ func IndexOf[T comparable](arr []T, val T) int {
|
||||
// LastIndexOf returns the index at which the last occurrence of the item is found in a slice or return -1 if the then cannot be found.
|
||||
// Play: https://go.dev/play/p/DokM4cf1IKH
|
||||
func LastIndexOf[T comparable](slice []T, item T) int {
|
||||
for i := len(slice) - 1; i > 0; i-- {
|
||||
for i := len(slice) - 1; i >= 0; i-- {
|
||||
if item == slice[i] {
|
||||
return i
|
||||
}
|
||||
|
||||
@@ -379,6 +379,25 @@ func ExampleForEach() {
|
||||
// [2 3 4]
|
||||
}
|
||||
|
||||
func ExampleForEachWithBreak() {
|
||||
numbers := []int{1, 2, 3, 4, 5}
|
||||
|
||||
var sum int
|
||||
|
||||
ForEachWithBreak(numbers, func(_, n int) bool {
|
||||
if n > 3 {
|
||||
return false
|
||||
}
|
||||
sum += n
|
||||
return true
|
||||
})
|
||||
|
||||
fmt.Println(sum)
|
||||
|
||||
// Output:
|
||||
// 6
|
||||
}
|
||||
|
||||
func ExampleMap() {
|
||||
nums := []int{1, 2, 3}
|
||||
|
||||
|
||||
@@ -307,6 +307,23 @@ func TestForEach(t *testing.T) {
|
||||
assert.Equal(expected, numbersAddTwo)
|
||||
}
|
||||
|
||||
func TestForEachWithBreak(t *testing.T) {
|
||||
numbers := []int{1, 2, 3, 4, 5}
|
||||
|
||||
var sum int
|
||||
|
||||
ForEachWithBreak(numbers, func(_, n int) bool {
|
||||
if n > 3 {
|
||||
return false
|
||||
}
|
||||
sum += n
|
||||
return true
|
||||
})
|
||||
|
||||
assert := internal.NewAssert(t, "TestForEach")
|
||||
assert.Equal(6, sum)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
nums := []int{1, 2, 3, 4}
|
||||
multiplyTwo := func(i, num int) int {
|
||||
@@ -648,8 +665,8 @@ func TestDifferenceWith(t *testing.T) {
|
||||
func TestDifferenceBy(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDifferenceBy")
|
||||
|
||||
s1 := []int{1, 2, 3, 4, 5} //after add one: 2 3 4 5 6
|
||||
s2 := []int{3, 4, 5} //after add one: 4 5 6
|
||||
s1 := []int{1, 2, 3, 4, 5} // after add one: 2 3 4 5 6
|
||||
s2 := []int{3, 4, 5} // after add one: 4 5 6
|
||||
addOne := func(i int, v int) int {
|
||||
return v + 1
|
||||
}
|
||||
@@ -856,8 +873,9 @@ func TestIndexOf(t *testing.T) {
|
||||
func TestLastIndexOf(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestLastIndexOf")
|
||||
|
||||
arr := []string{"a", "a", "b", "c"}
|
||||
assert.Equal(1, LastIndexOf(arr, "a"))
|
||||
arr := []string{"a", "b", "b", "c"}
|
||||
assert.Equal(0, LastIndexOf(arr, "a"))
|
||||
assert.Equal(2, LastIndexOf(arr, "b"))
|
||||
assert.Equal(-1, LastIndexOf(arr, "d"))
|
||||
}
|
||||
|
||||
|
||||
112
structs/field.go
Normal file
112
structs/field.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/duke-git/lancet/v2/pointer"
|
||||
)
|
||||
|
||||
// Field is abstract struct field for provide several high level functions
|
||||
type Field struct {
|
||||
Struct
|
||||
field reflect.StructField
|
||||
tag *Tag
|
||||
}
|
||||
|
||||
func newField(v reflect.Value, f reflect.StructField, tagName string) *Field {
|
||||
tag := f.Tag.Get(tagName)
|
||||
field := &Field{
|
||||
field: f,
|
||||
tag: newTag(tag),
|
||||
}
|
||||
field.rvalue = v
|
||||
field.rtype = v.Type()
|
||||
field.TagName = tagName
|
||||
return field
|
||||
}
|
||||
|
||||
// Tag returns the value that the key in the tag string.
|
||||
func (f *Field) Tag() *Tag {
|
||||
return f.tag
|
||||
}
|
||||
|
||||
// Value returns the underlying value of the field.
|
||||
func (f *Field) Value() any {
|
||||
return f.rvalue.Interface()
|
||||
}
|
||||
|
||||
// IsEmbedded returns true if the given field is an embedded field.
|
||||
func (f *Field) IsEmbedded() bool {
|
||||
return len(f.field.Index) > 1
|
||||
}
|
||||
|
||||
// IsExported returns true if the given field is exported.
|
||||
func (f *Field) IsExported() bool {
|
||||
return f.field.IsExported()
|
||||
}
|
||||
|
||||
// IsZero returns true if the given field is zero value.
|
||||
func (f *Field) IsZero() bool {
|
||||
z := reflect.Zero(f.rvalue.Type()).Interface()
|
||||
v := f.Value()
|
||||
return reflect.DeepEqual(z, v)
|
||||
}
|
||||
|
||||
// Name returns the name of the given field
|
||||
func (f *Field) Name() string {
|
||||
return f.field.Name
|
||||
}
|
||||
|
||||
// Kind returns the field's kind
|
||||
func (f *Field) Kind() reflect.Kind {
|
||||
return f.rvalue.Kind()
|
||||
}
|
||||
|
||||
// IsSlice check if a struct field type is slice or not
|
||||
func (f *Field) IsSlice() bool {
|
||||
k := f.rvalue.Kind()
|
||||
return k == reflect.Slice
|
||||
}
|
||||
|
||||
// mapValue covert field value to map
|
||||
func (f *Field) mapValue(value any) any {
|
||||
val := pointer.ExtractPointer(value)
|
||||
v := reflect.ValueOf(val)
|
||||
var ret any
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
s := New(val)
|
||||
s.TagName = f.TagName
|
||||
m, _ := s.ToMap()
|
||||
ret = m
|
||||
case reflect.Map:
|
||||
mapEl := v.Type().Elem()
|
||||
switch mapEl.Kind() {
|
||||
case reflect.Ptr, reflect.Array, reflect.Map, reflect.Slice, reflect.Chan:
|
||||
// iterate the map
|
||||
m := make(map[string]any, v.Len())
|
||||
for _, key := range v.MapKeys() {
|
||||
m[key.String()] = f.mapValue(v.MapIndex(key).Interface())
|
||||
}
|
||||
ret = m
|
||||
default:
|
||||
ret = v.Interface()
|
||||
}
|
||||
case reflect.Slice, reflect.Array:
|
||||
sEl := v.Type().Elem()
|
||||
switch sEl.Kind() {
|
||||
case reflect.Ptr, reflect.Array, reflect.Map, reflect.Slice, reflect.Chan:
|
||||
slices := make([]any, v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
slices[i] = f.mapValue(v.Index(i).Interface())
|
||||
}
|
||||
ret = slices
|
||||
default:
|
||||
ret = v.Interface()
|
||||
}
|
||||
default:
|
||||
ret = v.Interface()
|
||||
}
|
||||
return ret
|
||||
}
|
||||
272
structs/field_test.go
Normal file
272
structs/field_test.go
Normal file
@@ -0,0 +1,272 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestField_Tag(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestField_Tag")
|
||||
|
||||
type Parent struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
p1 := &Parent{"111"}
|
||||
|
||||
s := New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
tag := n.Tag()
|
||||
assert.Equal("name", tag.Name)
|
||||
assert.Equal(true, tag.HasOption("omitempty"))
|
||||
}
|
||||
|
||||
func TestField_Value(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestField_Value")
|
||||
|
||||
type Parent struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
p1 := &Parent{"111"}
|
||||
|
||||
s := New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
|
||||
assert.Equal("111", n.Value())
|
||||
}
|
||||
|
||||
func TestField_IsEmbedded(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestField_IsEmbedded")
|
||||
type Parent struct {
|
||||
Name string
|
||||
}
|
||||
type Child struct {
|
||||
Parent
|
||||
Age int
|
||||
}
|
||||
c1 := &Child{}
|
||||
c1.Name = "111"
|
||||
c1.Age = 11
|
||||
|
||||
s := New(c1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
assert.Equal(true, n.IsEmbedded())
|
||||
assert.Equal(false, a.IsEmbedded())
|
||||
}
|
||||
|
||||
func TestField_IsExported(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestField_IsEmbedded")
|
||||
|
||||
type Parent struct {
|
||||
Name string
|
||||
age int
|
||||
}
|
||||
p1 := &Parent{Name: "11", age: 11}
|
||||
s := New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("age")
|
||||
assert.Equal(true, n.IsExported())
|
||||
assert.Equal(false, a.IsExported())
|
||||
}
|
||||
|
||||
func TestField_IsZero(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestField_IsZero")
|
||||
|
||||
type Parent struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p1 := &Parent{Age: 11}
|
||||
s := New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
assert.Equal(true, n.IsZero())
|
||||
assert.Equal(false, a.IsZero())
|
||||
}
|
||||
|
||||
func TestField_Name(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestField_Name")
|
||||
|
||||
type Parent struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p1 := &Parent{Age: 11}
|
||||
s := New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
|
||||
assert.Equal("Name", n.Name())
|
||||
assert.Equal("Age", a.Name())
|
||||
}
|
||||
|
||||
func TestField_Kind(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestField_Kind")
|
||||
|
||||
type Parent struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p1 := &Parent{Age: 11}
|
||||
s := New(p1)
|
||||
n, _ := s.Field("Name")
|
||||
a, _ := s.Field("Age")
|
||||
|
||||
assert.Equal(reflect.String, n.Kind())
|
||||
assert.Equal(reflect.Int, a.Kind())
|
||||
}
|
||||
|
||||
func TestField_IsSlice(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestField_IsSlice")
|
||||
|
||||
type Parent struct {
|
||||
Name string
|
||||
arr []int
|
||||
}
|
||||
|
||||
p1 := &Parent{arr: []int{1, 2, 3}}
|
||||
s := New(p1)
|
||||
a, _ := s.Field("arr")
|
||||
|
||||
assert.Equal(true, a.IsSlice())
|
||||
}
|
||||
|
||||
func TestField_MapValue(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestField_MapValue")
|
||||
|
||||
t.Run("nested struct", func(t *testing.T) {
|
||||
type Child struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
type Parent struct {
|
||||
Name string `json:"name"`
|
||||
Child *Child `json:"child"`
|
||||
}
|
||||
|
||||
c1 := &Child{"11-1"}
|
||||
p1 := &Parent{
|
||||
Name: "11",
|
||||
Child: c1,
|
||||
}
|
||||
|
||||
s := New(p1)
|
||||
f, ok := s.Field("Child")
|
||||
val := f.mapValue(f.Value())
|
||||
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal(map[string]any{"name": "11-1"}, val)
|
||||
})
|
||||
|
||||
t.Run("nested ptr struct", func(t *testing.T) {
|
||||
type Child struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
type Parent struct {
|
||||
Name string `json:"name"`
|
||||
Child any `json:"child"`
|
||||
}
|
||||
c1 := &Child{"11-1"}
|
||||
c2 := &c1
|
||||
c3 := &c2
|
||||
p1 := &Parent{
|
||||
Name: "11",
|
||||
Child: c3,
|
||||
}
|
||||
|
||||
s := New(p1)
|
||||
f, ok := s.Field("Child")
|
||||
val := f.mapValue(f.Value())
|
||||
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal(map[string]any{"name": "11-1"}, val)
|
||||
})
|
||||
|
||||
t.Run("nested array", func(t *testing.T) {
|
||||
type Parent struct {
|
||||
Name string `json:"name"`
|
||||
Child []int `json:"child"`
|
||||
}
|
||||
|
||||
p1 := &Parent{
|
||||
Name: "11",
|
||||
Child: []int{1, 2, 3},
|
||||
}
|
||||
|
||||
s := New(p1)
|
||||
f, ok := s.Field("Child")
|
||||
val := f.mapValue(f.Value())
|
||||
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal([]int{1, 2, 3}, val)
|
||||
})
|
||||
|
||||
t.Run("nested array struct", func(t *testing.T) {
|
||||
type Child struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
type Parent struct {
|
||||
Name string `json:"name"`
|
||||
Child []*Child `json:"child"`
|
||||
}
|
||||
|
||||
c1 := &Child{"11-1"}
|
||||
c2 := &Child{"11-2"}
|
||||
|
||||
p1 := &Parent{
|
||||
Name: "11",
|
||||
Child: []*Child{c1, c2},
|
||||
}
|
||||
|
||||
s := New(p1)
|
||||
f, ok := s.Field("Child")
|
||||
val := f.mapValue(f.Value())
|
||||
|
||||
assert.Equal(true, ok)
|
||||
arr := []any{map[string]any{"name": "11-1"}, map[string]any{"name": "11-2"}}
|
||||
assert.Equal(arr, val)
|
||||
})
|
||||
|
||||
t.Run("nested ptr array struct", func(t *testing.T) {
|
||||
type Child struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
type Parent struct {
|
||||
Name string `json:"name"`
|
||||
Child *[]*Child `json:"child"`
|
||||
}
|
||||
|
||||
c1 := &Child{"11-1"}
|
||||
c2 := &Child{"11-2"}
|
||||
|
||||
p1 := &Parent{
|
||||
Name: "11",
|
||||
Child: &[]*Child{c1, c2},
|
||||
}
|
||||
|
||||
s := New(p1)
|
||||
f, ok := s.Field("Child")
|
||||
val := f.mapValue(f.Value())
|
||||
|
||||
assert.Equal(true, ok)
|
||||
arr := []any{map[string]any{"name": "11-1"}, map[string]any{"name": "11-2"}}
|
||||
assert.Equal(arr, val)
|
||||
})
|
||||
|
||||
t.Run("nested map in struct", func(t *testing.T) {
|
||||
type Parent struct {
|
||||
Name string `json:"name"`
|
||||
Child map[string]any `json:"child"`
|
||||
}
|
||||
p1 := &Parent{
|
||||
Name: "11",
|
||||
Child: map[string]any{"a": 1, "b": map[string]any{"name": "11-1"}},
|
||||
}
|
||||
|
||||
s := New(p1)
|
||||
f, ok := s.Field("Child")
|
||||
val := f.mapValue(f.Value())
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal(map[string]any{"a": 1, "b": map[string]any{"name": "11-1"}}, val)
|
||||
})
|
||||
}
|
||||
117
structs/struct.go
Normal file
117
structs/struct.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// Package structs provide several high level functions to manipulate struct, tag, and field.
|
||||
package structs
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/duke-git/lancet/v2/pointer"
|
||||
)
|
||||
|
||||
// defaultTagName is the default tag for struct fields to lookup.
|
||||
var defaultTagName = "json"
|
||||
|
||||
// Struct is abstract struct for provide several high level functions
|
||||
type Struct struct {
|
||||
raw any
|
||||
rtype reflect.Type
|
||||
rvalue reflect.Value
|
||||
TagName string
|
||||
}
|
||||
|
||||
// New returns a new *Struct
|
||||
func New(value any, tagName ...string) *Struct {
|
||||
value = pointer.ExtractPointer(value)
|
||||
v := reflect.ValueOf(value)
|
||||
t := reflect.TypeOf(value)
|
||||
|
||||
tn := defaultTagName
|
||||
|
||||
if len(tagName) > 0 {
|
||||
tn = tagName[0]
|
||||
}
|
||||
|
||||
// if need: can also set defaultTagName to tn across structs package level
|
||||
// defaultTagName = tn
|
||||
|
||||
return &Struct{
|
||||
raw: value,
|
||||
rtype: t,
|
||||
rvalue: v,
|
||||
TagName: tn,
|
||||
}
|
||||
}
|
||||
|
||||
// ToMap converts the given struct to a map[string]any, where the keys
|
||||
// of the keys are the field names and the values of the map are the values
|
||||
// of the fields. The default map key is the struct field name, but you can
|
||||
// change it. The `json` key is the default tag key. Example:
|
||||
//
|
||||
// // default
|
||||
// Name string `json:"name"`
|
||||
//
|
||||
// // ignore the field
|
||||
// Name string // no tag
|
||||
// Age string `json:"-"` // json ignore tag
|
||||
// sex string // unexported field
|
||||
// Goal int `json:"goal,omitempty"` // omitempty if the field is zero value
|
||||
//
|
||||
// // custom map key
|
||||
// Name string `json:"myName"`
|
||||
//
|
||||
// ToMap convert the exported fields of a struct to map.
|
||||
func (s *Struct) ToMap() (map[string]any, error) {
|
||||
if !s.IsStruct() {
|
||||
return nil, errInvalidStruct(s)
|
||||
}
|
||||
|
||||
result := make(map[string]any)
|
||||
fields := s.Fields()
|
||||
for _, f := range fields {
|
||||
if !f.IsExported() || f.tag.IsEmpty() || f.tag.Name == "-" {
|
||||
continue
|
||||
}
|
||||
if f.IsZero() && f.tag.HasOption("omitempty") {
|
||||
continue
|
||||
}
|
||||
result[f.tag.Name] = f.mapValue(f.Value())
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Fields returns all the struct fields within a slice
|
||||
func (s *Struct) Fields() []*Field {
|
||||
var fields []*Field
|
||||
fieldNum := s.rvalue.NumField()
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
v := s.rvalue.Field(i)
|
||||
sf := s.rtype.Field(i)
|
||||
field := newField(v, sf, s.TagName)
|
||||
fields = append(fields, field)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
// Field returns a Field if the given field name was found
|
||||
func (s *Struct) Field(name string) (*Field, bool) {
|
||||
f, ok := s.rtype.FieldByName(name)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return newField(s.rvalue.FieldByName(name), f, s.TagName), true
|
||||
}
|
||||
|
||||
// IsStruct returns true if the given rvalue is a struct
|
||||
func (s *Struct) IsStruct() bool {
|
||||
k := s.rvalue.Kind()
|
||||
if k == reflect.Invalid {
|
||||
return false
|
||||
}
|
||||
return k == reflect.Struct
|
||||
}
|
||||
|
||||
// ToMap convert struct to map, only convert exported struct field
|
||||
// map key is specified same as struct field tag `json` value.
|
||||
func ToMap(v any) (map[string]any, error) {
|
||||
return New(v).ToMap()
|
||||
}
|
||||
7
structs/struct_internal.go
Normal file
7
structs/struct_internal.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package structs
|
||||
|
||||
import "fmt"
|
||||
|
||||
func errInvalidStruct(v any) error {
|
||||
return fmt.Errorf("invalid struct %v", v)
|
||||
}
|
||||
132
structs/struct_test.go
Normal file
132
structs/struct_test.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestStruct_ToMap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStruct_ToMap")
|
||||
|
||||
t.Run("invalid struct", func(t *testing.T) {
|
||||
m, _ := ToMap(1)
|
||||
var expected map[string]any
|
||||
assert.Equal(expected, m)
|
||||
})
|
||||
|
||||
t.Run("StructToMap", func(_ *testing.T) {
|
||||
type People struct {
|
||||
Name string `json:"name"`
|
||||
age int
|
||||
}
|
||||
p := &People{
|
||||
"test",
|
||||
100,
|
||||
}
|
||||
pm, _ := ToMap(p)
|
||||
var expected = map[string]any{"name": "test"}
|
||||
assert.Equal(expected, pm)
|
||||
})
|
||||
|
||||
t.Run("StructToMapWithJsonAttr", func(_ *testing.T) {
|
||||
type People struct {
|
||||
Name string `json:"name,omitempty"` // json tag with attribute
|
||||
Phone string `json:"phone"` // json tag without attribute
|
||||
Sex string `json:"-"` // ignore by "-"
|
||||
Age int // ignore by no tag
|
||||
email string // ignore by unexported
|
||||
IsWorking bool `json:"is_working"`
|
||||
}
|
||||
p1 := People{
|
||||
Name: "AAA", // exist
|
||||
Phone: "1111",
|
||||
Sex: "male",
|
||||
Age: 100,
|
||||
email: "11@gmail.com",
|
||||
}
|
||||
p1m, _ := ToMap(p1)
|
||||
var expect1 = map[string]any{"name": "AAA", "phone": "1111", "is_working": false}
|
||||
assert.Equal(expect1, p1m)
|
||||
|
||||
p2 := People{
|
||||
Name: "",
|
||||
Phone: "2222",
|
||||
Sex: "male",
|
||||
Age: 0,
|
||||
email: "22@gmail.com",
|
||||
IsWorking: true,
|
||||
}
|
||||
p2m, _ := ToMap(p2)
|
||||
var expect2 = map[string]any{"phone": "2222", "is_working": true}
|
||||
assert.Equal(expect2, p2m)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStruct_Fields(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStruct_Fields")
|
||||
|
||||
type Parent struct {
|
||||
A string `json:"a"`
|
||||
B int `json:"b"`
|
||||
C []string `json:"c"`
|
||||
D map[string]any `json:"d"`
|
||||
}
|
||||
|
||||
p1 := &Parent{
|
||||
A: "1",
|
||||
B: 11,
|
||||
C: []string{"11", "22"},
|
||||
D: map[string]any{"d1": 1, "d2": 2},
|
||||
}
|
||||
|
||||
s := New(p1)
|
||||
fields := s.Fields()
|
||||
assert.Equal(4, len(fields))
|
||||
assert.Equal(reflect.String, fields[0].Kind())
|
||||
assert.Equal(reflect.Int, fields[1].Kind())
|
||||
assert.Equal(reflect.Slice, fields[2].Kind())
|
||||
assert.Equal(reflect.Map, fields[3].Kind())
|
||||
}
|
||||
|
||||
func TestStruct_Field(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStruct_Field")
|
||||
|
||||
type Parent struct {
|
||||
A string `json:"a"`
|
||||
B int `json:"b"`
|
||||
C []string `json:"c"`
|
||||
D map[string]any `json:"d"`
|
||||
}
|
||||
|
||||
p1 := &Parent{
|
||||
A: "1",
|
||||
B: 11,
|
||||
C: []string{"11", "22"},
|
||||
D: map[string]any{"d1": 1, "d2": 2},
|
||||
}
|
||||
|
||||
s := New(p1)
|
||||
a, ok := s.Field("A")
|
||||
assert.Equal(true, ok)
|
||||
assert.Equal(reflect.String, a.Kind())
|
||||
assert.Equal("1", a.Value())
|
||||
assert.Equal("a", a.tag.Name)
|
||||
assert.Equal(false, a.tag.HasOption("omitempty"))
|
||||
assert.Equal(false, a.tag.IsEmpty())
|
||||
}
|
||||
|
||||
func TestStruct_IsStruct(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStruct_Field")
|
||||
|
||||
type Test1 struct{}
|
||||
t1 := &Test1{}
|
||||
t2 := 1
|
||||
|
||||
s1 := New(t1)
|
||||
s2 := New(t2)
|
||||
|
||||
assert.Equal(true, s1.IsStruct())
|
||||
assert.Equal(false, s2.IsStruct())
|
||||
}
|
||||
36
structs/tag.go
Normal file
36
structs/tag.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
|
||||
// Tag is abstract struct field tag
|
||||
type Tag struct {
|
||||
Name string
|
||||
Options []string
|
||||
}
|
||||
|
||||
func newTag(tag string) *Tag {
|
||||
res := strings.Split(tag, ",")
|
||||
return &Tag{
|
||||
Name: res[0],
|
||||
Options: res[1:],
|
||||
}
|
||||
}
|
||||
|
||||
// HasOption check if a struct field tag has option setting.
|
||||
func (t *Tag) HasOption(opt string) bool {
|
||||
for _, o := range t.Options {
|
||||
if o == opt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsEmpty check if a struct field has tag setting.
|
||||
func (t *Tag) IsEmpty() bool {
|
||||
return validator.IsEmptyString(t.Name)
|
||||
}
|
||||
@@ -360,3 +360,16 @@ func WordCount(s string) int {
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// RemoveNonPrintable remove non-printable characters from a string.
|
||||
// Play: todo
|
||||
func RemoveNonPrintable(str string) string {
|
||||
result := strings.Map(func(r rune) rune {
|
||||
if unicode.IsPrint(r) {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}, str)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -438,3 +438,14 @@ func ExampleWordCount() {
|
||||
// 0
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleRemoveNonPrintable() {
|
||||
result1 := RemoveNonPrintable("hello\u00a0 \u200bworld\n")
|
||||
result2 := RemoveNonPrintable("你好😄")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
// Output:
|
||||
// hello world
|
||||
// 你好😄
|
||||
}
|
||||
|
||||
@@ -342,3 +342,10 @@ func TestWordCount(t *testing.T) {
|
||||
assert.Equal(v, WordCount(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveNonPrintable(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestRemoveNonPrintable")
|
||||
|
||||
assert.Equal("hello world", RemoveNonPrintable("hello\u00a0 \u200bworld\n"))
|
||||
assert.Equal("你好😄", RemoveNonPrintable("你好😄"))
|
||||
}
|
||||
|
||||
@@ -58,6 +58,31 @@ func IsAllLower(str string) bool {
|
||||
return str != ""
|
||||
}
|
||||
|
||||
// IsASCII checks if string is all ASCII char.
|
||||
// Play: todo
|
||||
func IsASCII(str string) bool {
|
||||
for i := 0; i < len(str); i++ {
|
||||
if str[i] > unicode.MaxASCII {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsPrintable checks if string is all printable chars.
|
||||
// Play: todo
|
||||
func IsPrintable(str string) bool {
|
||||
for _, r := range str {
|
||||
if !unicode.IsPrint(r) {
|
||||
if r == '\n' || r == '\r' || r == '\t' || r == '`' {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ContainUpper check if the string contain at least one upper case letter A-Z.
|
||||
// Play: https://go.dev/play/p/CmWeBEk27-z
|
||||
func ContainUpper(str string) bool {
|
||||
|
||||
@@ -407,3 +407,45 @@ func ExampleIsGBK() {
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleIsASCII() {
|
||||
result1 := IsASCII("ABC")
|
||||
result2 := IsASCII("123")
|
||||
result3 := IsASCII("")
|
||||
result4 := IsASCII("😄")
|
||||
result5 := IsASCII("你好")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleIsPrintable() {
|
||||
result1 := IsPrintable("ABC")
|
||||
result2 := IsPrintable("{id: 123}")
|
||||
result3 := IsPrintable("")
|
||||
result4 := IsPrintable("😄")
|
||||
result5 := IsPrintable("\u0000")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
@@ -401,3 +401,23 @@ func TestIsGBK(t *testing.T) {
|
||||
assert.Equal(true, IsGBK(gbkData))
|
||||
assert.Equal(false, utf8.Valid(gbkData))
|
||||
}
|
||||
|
||||
func TestIsASCII(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsASCII")
|
||||
|
||||
assert.Equal(true, IsASCII("ABC"))
|
||||
assert.Equal(true, IsASCII("123"))
|
||||
assert.Equal(true, IsASCII(""))
|
||||
assert.Equal(false, IsASCII("😄"))
|
||||
assert.Equal(false, IsASCII("你好"))
|
||||
}
|
||||
|
||||
func TestIsPrintable(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsPrintable")
|
||||
|
||||
assert.Equal(true, IsPrintable("ABC"))
|
||||
assert.Equal(true, IsPrintable("{id: 123}"))
|
||||
assert.Equal(true, IsPrintable(""))
|
||||
assert.Equal(true, IsPrintable("😄"))
|
||||
assert.Equal(false, IsPrintable("\u0000"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user