mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 12:52:28 +08:00
Compare commits
117 Commits
| 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 | ||
|
|
71aa91a58d | ||
|
|
081908bce3 | ||
|
|
66efe61834 | ||
|
|
94f7f3e0e5 | ||
|
|
9d2c9806d1 | ||
|
|
e523d4af6e | ||
|
|
f3801bcd8f | ||
|
|
5767aad303 | ||
|
|
f7aaa1ed2a | ||
|
|
ef19a414bc | ||
|
|
65704dea06 | ||
|
|
5eac358bb0 | ||
|
|
8e36ef5cef | ||
|
|
21b0d2ec0e | ||
|
|
32ca975204 | ||
|
|
ec740e442c | ||
|
|
ed98ad53ec | ||
|
|
af7b9d2710 | ||
|
|
f08b368001 | ||
|
|
be8a0558f8 | ||
|
|
46de539e22 | ||
|
|
36fb3abe9e | ||
|
|
fee8d325b7 | ||
|
|
8784be1583 | ||
|
|
84cd68de7f | ||
|
|
a774c060ce | ||
|
|
730f061b95 | ||
|
|
2eb08f3404 | ||
|
|
4c1496b648 | ||
|
|
c2ae784f27 | ||
|
|
e71cecefea | ||
|
|
572e53aa14 | ||
|
|
ca9ecb9c8a | ||
|
|
8bf0786abe | ||
|
|
61cb8395e3 | ||
|
|
dde8a41daf | ||
|
|
d7518e01af | ||
|
|
be9fa7acaa | ||
|
|
2629a731cc | ||
|
|
54c7f90b7f | ||
|
|
770bc88b88 | ||
|
|
4cc1722f81 | ||
|
|
d0260b2841 | ||
|
|
57bd64cae7 | ||
|
|
c383719496 | ||
|
|
4b196a72b1 | ||
|
|
888381a06c | ||
|
|
a554eb7ef4 | ||
|
|
26ff90122b | ||
|
|
75b27c6540 | ||
|
|
a7e77fa98d | ||
|
|
abf392117a | ||
|
|
d231d9f572 | ||
|
|
97447d058e | ||
|
|
040e112aa6 | ||
|
|
afb021b4b5 | ||
|
|
5075774000 | ||
|
|
6bba44dc50 | ||
|
|
422022c74d | ||
|
|
87fcf97e2d | ||
|
|
05d1f348d4 | ||
|
|
6f2f1f3004 |
202
README.md
202
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)
|
||||
@@ -24,7 +24,7 @@ English | [简体中文](./README_zh-CN.md)
|
||||
## Feature
|
||||
|
||||
- 👏 Comprehensive, efficient and reusable.
|
||||
- 💪 300+ go util functions, support string, slice, datetime, net, crypt...
|
||||
- 💪 400+ go util functions, support string, slice, datetime, net, crypt...
|
||||
- 💅 Only depend on the go standard library.
|
||||
- 🌍 Unit test for every exported function.
|
||||
|
||||
@@ -38,10 +38,10 @@ English | [简体中文](./README_zh-CN.md)
|
||||
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
|
||||
```
|
||||
|
||||
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.3.5. </b>
|
||||
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.3.7. </b>
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet@v1.3.5 // below go1.18, install latest version of v1.x.x
|
||||
go get github.com/duke-git/lancet@v1.3.7 // below go1.18, install latest version of v1.x.x
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -246,6 +246,12 @@ import "github.com/duke-git/lancet/v2/convertor"
|
||||
- **<big>DecodeByte</big>** : decode byte slice data to target object.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#DecodeByte)]
|
||||
[[play](https://go.dev/play/p/zI6xsmuQRbn)]
|
||||
- **<big>DeepClone</big>** : creates a deep copy of passed item, can't clone unexported field of struct.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#DeepClone)]
|
||||
[[play](https://go.dev/play/p/j4DP5dquxnk)]
|
||||
- **<big>CopyProperties</big>** : copies each field from the source struct into the destination struct.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#CopyProperties)]
|
||||
[[play](https://go.dev/play/p/FOVY3XJL-6B)]
|
||||
|
||||
### 5. Cryptor package is for data encryption and decryption.
|
||||
|
||||
@@ -450,9 +456,6 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#ToIso8601)]
|
||||
[[play](https://go.dev/play/p/mkhOHQkdeA2)]
|
||||
|
||||
|
||||
|
||||
|
||||
### 7. Datastructure package constains some common data structure. eg. list, linklist, stack, queue, set, tree, graph.
|
||||
|
||||
```go
|
||||
@@ -587,7 +590,6 @@ import "github.com/duke-git/lancet/v2/function"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function.md#Watcher)]
|
||||
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
|
||||
|
||||
|
||||
### 11. Maputil package includes some functions to manipulate map.
|
||||
|
||||
```go
|
||||
@@ -602,12 +604,30 @@ import "github.com/duke-git/lancet/v2/maputil"
|
||||
- **<big>Filter</big>** : iterates over map, return a new map contains all key and value pairs pass the predicate function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Filter)]
|
||||
[[play](https://go.dev/play/p/fSvF3wxuNG7)]
|
||||
- **<big>FilterByKeys</big>** : iterates over map, return a new map whose keys are all given keys
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#FilterByKeys)]
|
||||
[[play](https://go.dev/play/p/7ov6BJHbVqh)]
|
||||
- **<big>FilterByValues</big>** : iterates over map, return a new map whose values are all given values.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#FilterByValues)]
|
||||
[[play](https://go.dev/play/p/P3-9MdcXegR)]
|
||||
- **<big>OmitBy</big>** : the opposite of Filter, removes all the map elements for which the predicate function returns true.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#OmitBy)]
|
||||
[[play](https://go.dev/play/p/YJM4Hj5hNwm)]
|
||||
- **<big>OmitByKeys</big>** : the opposite of FilterByKeys, extracts all the map elements which keys are not omitted.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#OmitByKeys)]
|
||||
[[play](https://go.dev/play/p/jXGrWDBfSRp)]
|
||||
- **<big>OmitByValues</big>** : the opposite of FilterByValues. remov all elements whose value are in the give slice.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#OmitByValues)]
|
||||
[[play](https://go.dev/play/p/XB7Y10uw20_U)]
|
||||
- **<big>Intersect</big>** : iterates over maps, return a new map of key and value pairs in all given maps.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Intersect)]
|
||||
[[play](https://go.dev/play/p/Zld0oj3sjcC)]
|
||||
- **<big>Keys</big>** : returns a slice of the map's keys.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Keys)]
|
||||
[[play](https://go.dev/play/p/xNB5bTb97Wd)]
|
||||
- **<big>KeysBy</big>** : creates a slice whose element is the result of function mapper invoked by every map's key.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#KeysBy)]
|
||||
[[play](https://go.dev/play/p/hI371iB8Up8)]
|
||||
- **<big>Merge</big>** : merge maps, next key will overwrite previous key.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Merge)]
|
||||
[[play](https://go.dev/play/p/H95LENF1uB-)]
|
||||
@@ -617,6 +637,24 @@ import "github.com/duke-git/lancet/v2/maputil"
|
||||
- **<big>Values</big>** : returns a slice of the map's values.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Values)]
|
||||
[[play](https://go.dev/play/p/CBKdUc5FTW6)]
|
||||
- **<big>ValuesBy</big>** : creates a slice whose element is the result of function mapper invoked by every map's value.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#ValuesBy)]
|
||||
[[play](https://go.dev/play/p/sg9-oRidh8f)]
|
||||
- **<big>MapKeys</big>** : transforms a map to other type map by manipulating it's keys.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#MapKeys)]
|
||||
[[play](https://go.dev/play/p/8scDxWeBDKd)]
|
||||
- **<big>MapValues</big>** : transforms a map to other type map by manipulating it's values.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#MapValues)]
|
||||
[[play](https://go.dev/play/p/g92aY3fc7Iw)]
|
||||
- **<big>Entries</big>** : transforms a map into array of key/value pairs.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Entries)]
|
||||
[[play](https://go.dev/play/p/Ltb11LNcElY)]
|
||||
- **<big>FromEntries</big>** : creates a map based on a slice of key/value pairs.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#FromEntries)]
|
||||
[[play](https://go.dev/play/p/fTdu4sCNjQO)]
|
||||
- **<big>Transform</big>** : transform a map to another type map.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Transform)]
|
||||
[[play](https://go.dev/play/p/P6ovfToM3zj)]
|
||||
- **<big>IsDisjoint</big>** : check two map are disjoint if they have no keys in common.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#IsDisjoint)]
|
||||
[[play](https://go.dev/play/p/N9qgYg_Ho6f)]
|
||||
@@ -665,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.
|
||||
|
||||
@@ -729,8 +781,6 @@ import "github.com/duke-git/lancet/v2/netutil"
|
||||
- **<big>ParseHttpResponse</big>** : decode http response into target object.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#ParseHttpResponse)]
|
||||
|
||||
|
||||
|
||||
### 14. Random package implements some basic functions to generate random int and string.
|
||||
|
||||
```go
|
||||
@@ -802,6 +852,9 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Contain</big>** : check if the value is in the slice or not.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Contain)]
|
||||
[[play](https://go.dev/play/p/_454yEHcNjf)]
|
||||
- **<big>ContainBy</big>** : returns true if predicate function return true.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ContainBy)]
|
||||
[[play](https://go.dev/play/p/49tkHfX4GNc)]
|
||||
- **<big>ContainSubSlice</big>** : check if the slice contain a given subslice or not.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ContainSubSlice)]
|
||||
[[play](https://go.dev/play/p/bcuQ3UT6Sev)]
|
||||
@@ -832,9 +885,18 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>DeleteAt</big>** : delete the element of slice from specific start index to end index - 1.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#DeleteAt)]
|
||||
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
|
||||
- **<big>Drop</big>** : creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0.
|
||||
- **<big>Drop</big>** : drop n elements from the start of a slice.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Drop)]
|
||||
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
|
||||
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
|
||||
- **<big>DropRight</big>** : drop n elements from the end of a slice.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#DropRight)]
|
||||
[[play](https://go.dev/play/p/8bcXvywZezG)]
|
||||
- **<big>DropWhile</big>** : drop n elements from the start of a slice while predicate function returns true.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#DropWhile)]
|
||||
[[play](https://go.dev/play/p/4rt252UV_qs)]
|
||||
- **<big>DropRightWhile</big>** : drop n elements from the end of a slice while predicate function returns true.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#DropRightWhile)]
|
||||
[[play](https://go.dev/play/p/6wyK3zMY56e)]
|
||||
- **<big>Equal</big>** : checks if two slices are equal: the same length and all elements' order and value are equal.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Equal)]
|
||||
[[play](https://go.dev/play/p/WcRQJ37ifPa)]
|
||||
@@ -847,6 +909,9 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Filter</big>** : iterates over elements of slice, returning an slice of all elements pass the predicate function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Filter)]
|
||||
[[play](https://go.dev/play/p/SdPna-7qK4T)]
|
||||
- **<big>FilterMap</big>** : returns a slice which apply both filtering and mapping to the given slice.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#FilterMap)]
|
||||
[[play](https://go.dev/play/p/J94SZ_9MiIe)]
|
||||
- **<big>Find</big>** : iterates over elements of slice, returning the first one that passes a truth test on predicate function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Find)]
|
||||
[[play](https://go.dev/play/p/CBKeBoHVLgq)]
|
||||
@@ -859,9 +924,15 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>FlattenDeep</big>** : flattens slice recursive to one level.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#FlattenDeep)]
|
||||
[[play](https://go.dev/play/p/yjYNHPyCFaF)]
|
||||
- **<big>FlatMap</big>** : manipulates a slice and transforms and flattens it to a slice of another type.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#FlatMap)]
|
||||
[[play](https://go.dev/play/p/_QARWlWs1N_F)]
|
||||
- **<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)]
|
||||
@@ -910,6 +981,18 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Shuffle</big>** : shuffle the slice.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Shuffle)]
|
||||
[[play](https://go.dev/play/p/YHvhnWGU3Ge)]
|
||||
- **<big>IsAscending</big>** : Checks if a slice is ascending order.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#IsAscending)]
|
||||
[[play](https://go.dev/play/p/9CtsFjet4SH)]
|
||||
- **<big>IsDescending</big>** : Checks if a slice is descending order.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#IsDescending)]
|
||||
[[play](https://go.dev/play/p/U_LljFXma14)]
|
||||
- **<big>IsSorted</big>** : Checks if a slice is sorted (ascending or descending).
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#IsSorted)]
|
||||
[[play](https://go.dev/play/p/nCE8wPLwSA-)]
|
||||
- **<big>IsSortedByKey</big>** : Checks if a slice is sorted by iteratee function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#IsSortedByKey)]
|
||||
[[play](https://go.dev/play/p/tUoGB7DOHI4)]
|
||||
- **<big>Sort</big>** : sorts a slice of any ordered type(number or string).
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Sort)]
|
||||
[[play](https://go.dev/play/p/V9AVjzf_4Fk)]
|
||||
@@ -956,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"
|
||||
@@ -996,6 +1112,9 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- **<big>UpperFirst</big>** : converts the first character of string to upper case.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#UpperFirst)]
|
||||
[[play](https://go.dev/play/p/sBbBxRbs8MM)]
|
||||
- **<big>Pad</big>** : pads string on the left and right side if it's shorter than size.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Pad)]
|
||||
[[play](https://go.dev/play/p/NzImQq-VF8q)]
|
||||
- **<big>PadEnd</big>** : pads string with given characters on the right side if it's shorter than limit size. Padding characters are truncated if they exceed size.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#PadEnd)]
|
||||
[[play](https://go.dev/play/p/9xP8rN0vz--)]
|
||||
@@ -1023,6 +1142,14 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- **<big>Unwrap</big>** : unwrap a given string from anther string. will change source string.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Unwrap)]
|
||||
[[play](https://go.dev/play/p/Ec2q4BzCpG-)]
|
||||
- **<big>SplitWords</big>** : splits a string into words, word only contains alphabetic characters.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#SplitWords)]
|
||||
[[play](https://go.dev/play/p/KLiX4WiysMM)]
|
||||
- **<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.
|
||||
|
||||
@@ -1060,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"
|
||||
@@ -1152,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"
|
||||
@@ -1161,10 +1292,45 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
|
||||
#### Function list:
|
||||
|
||||
- **<big>Unwrap</big>** : check if err is nil then it returns a valid value. If err is not nil, Unwrap panics with err.
|
||||
- **<big>New</big>** : creates a new XError pointer instance with message.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#New)]
|
||||
[[play](https://go.dev/play/p/w4oWZts7q7f)]
|
||||
- **<big>Wrap</big>** : creates a new XError pointer instance based on error object, and add message.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#Wrap)]
|
||||
[[play](https://go.dev/play/p/5385qT2dCi4)]
|
||||
- **<big>Unwrap</big>** : returns unwrapped XError from err by errors.As. If no XError, returns nil.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#Unwrap)]
|
||||
[[play](https://go.dev/play/p/w84d7Mb3Afk)]
|
||||
|
||||
[[play](https://go.dev/play/p/LKMLep723tu)]
|
||||
- **<big>XError_Wrap</big>** : creates a new XError and copy message and id to new one.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#XError_Wrap)]
|
||||
[[play](https://go.dev/play/p/5385qT2dCi4)]
|
||||
- **<big>XError_Unwrap</big>** : Compatible with github.com/pkg/errors.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#XError_Unwrap)]
|
||||
[[play](https://go.dev/play/p/VUXJ8BST4c6)]
|
||||
- **<big>XError_With</big>** : adds key and value related to the XError object.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#XError_With)]
|
||||
[[play](https://go.dev/play/p/ow8UISXX_Dp)]
|
||||
- **<big>XError_Id</big>** : sets XError object id to check equality in XError.Is.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#XError_Id)]
|
||||
[[play](https://go.dev/play/p/X6HBlsy58U9)]
|
||||
- **<big>XError_Is</big>** : checks if target error is XError and Error.id of two errors are matched.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#XError_Is)]
|
||||
[[play](https://go.dev/play/p/X6HBlsy58U9)]
|
||||
- **<big>XError_Values</big>** : returns map of key and value that is set by XError.With function.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#XError_Values)]
|
||||
[[play](https://go.dev/play/p/ow8UISXX_Dp)]
|
||||
- **<big>XError_StackTrace</big>** : returns stack trace which is compatible with pkg/errors.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#XError_StackTrace)]
|
||||
[[play](https://go.dev/play/p/6FAvSQpa7pc)]
|
||||
- **<big>XError_Info</big>** : returns information of xerror, which can be printed.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#XError_Info)]
|
||||
[[play](https://go.dev/play/p/1ZX0ME1F-Jb)]
|
||||
- **<big>XError_Error</big>** : implements standard error interface.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#XError_Error)]
|
||||
[[play](https://go.dev/play/p/w4oWZts7q7f)]
|
||||
- **<big>TryUnwrap</big>** : check if err is nil then it returns a valid value. If err is not nil, TryUnwrap panics with err.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#TryUnwrap)]
|
||||
[[play](https://go.dev/play/p/acyZVkNZEeW)]
|
||||
|
||||
## How to Contribute
|
||||
|
||||
|
||||
435
README_zh-CN.md
435
README_zh-CN.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)
|
||||
@@ -23,7 +23,7 @@
|
||||
## 特性
|
||||
|
||||
- 👏 全面、高效、可复用
|
||||
- 💪 300+常用 go 工具函数,支持 string、slice、datetime、net、crypt...
|
||||
- 💪 400+常用 go 工具函数,支持 string、slice、datetime、net、crypt...
|
||||
- 💅 只依赖 go 标准库
|
||||
- 🌍 所有导出函数单元测试覆盖率 100%
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
|
||||
```
|
||||
|
||||
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.3.5。</b>
|
||||
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.3.7。</b>
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet@v1.3.5 // 使用go1.18以下版本, 必须安装v1.x.x版本
|
||||
go get github.com/duke-git/lancet@v1.3.7 // 使用go1.18以下版本, 必须安装v1.x.x版本
|
||||
```
|
||||
|
||||
## 用法
|
||||
@@ -181,7 +181,7 @@ import "github.com/duke-git/lancet/v2/condition"
|
||||
[[play](https://go.dev/play/p/g2j08F_zZky)
|
||||
- **<big>Xnor</big>** : 如果 a 和 b 都是真的或 a 和 b 均是假的,则返回 true。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/condition_zh-CN.md#Xnor)]
|
||||
[[play](https://go.dev/play/p/OuDB9g51643)]]
|
||||
[[play](https://go.dev/play/p/OuDB9g51643)]]
|
||||
- **<big>Nand</big>** : 如果 a 和 b 都为真,返回 false,否则返回 true
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/condition_zh-CN.md#Nand)]
|
||||
[[play](https://go.dev/play/p/vSRMLxLIbq8)]
|
||||
@@ -245,6 +245,12 @@ import "github.com/duke-git/lancet/v2/convertor"
|
||||
- **<big>DecodeByte</big>** : 解码字节切片到目标对象,目标对象需要传入一个指针实例。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#DecodeByte)]
|
||||
[[play](https://go.dev/play/p/zI6xsmuQRbn)]
|
||||
- **<big>DeepClone</big>** : 创建一个传入值的深拷贝, 无法克隆结构体的非导出字段。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#DeepClone)]
|
||||
[[play](https://go.dev/play/p/j4DP5dquxnk)]
|
||||
- **<big>CopyProperties</big>** : 拷贝不同结构体之间的同名字段。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#CopyProperties)]
|
||||
[[play](https://go.dev/play/p/FOVY3XJL-6B)]
|
||||
|
||||
### 5. cryptor 加密包支持数据加密和解密,获取 md5,hash 值。支持 base64, md5, hmac, aes, des, rsa。
|
||||
|
||||
@@ -254,103 +260,101 @@ import "github.com/duke-git/lancet/v2/cryptor"
|
||||
|
||||
#### 函数列表:
|
||||
|
||||
- **<big>AesEcbEncrypt</big>** : 使用AES ECB算法模式加密数据。
|
||||
- **<big>AesEcbEncrypt</big>** : 使用 AES ECB 算法模式加密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesEcbEncrypt)]
|
||||
[[play](https://go.dev/play/p/zI6xsmuQRbn)]
|
||||
- **<big>AesEcbDecrypt</big>** : 使用AES ECB算法模解密数据。
|
||||
- **<big>AesEcbDecrypt</big>** : 使用 AES ECB 算法模解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesEcbDecrypt)]
|
||||
[[play](https://go.dev/play/p/zI6xsmuQRbn)]
|
||||
- **<big>AesCbcEncrypt</big>** : 使用AES CBC算法模式加密数据。
|
||||
- **<big>AesCbcEncrypt</big>** : 使用 AES CBC 算法模式加密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesCbcEncrypt)]
|
||||
[[play](https://go.dev/play/p/IOq_g8_lKZD)]
|
||||
- **<big>AesCbcDecrypt</big>** : 使用AES CBC算法模式解密数据。
|
||||
- **<big>AesCbcDecrypt</big>** : 使用 AES CBC 算法模式解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesCbcDecrypt)]
|
||||
[[play](https://go.dev/play/p/IOq_g8_lKZD)]
|
||||
- **<big>AesCtrCrypt</big>** : 使用AES CTR算法模式加密/解密数据。
|
||||
- **<big>AesCtrCrypt</big>** : 使用 AES CTR 算法模式加密/解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesCtrCrypt)]
|
||||
[[play](https://go.dev/play/p/SpaZO0-5Nsp)]
|
||||
- **<big>AesCfbEncrypt</big>** : 使用AES CFB算法模式加密数据。
|
||||
- **<big>AesCfbEncrypt</big>** : 使用 AES CFB 算法模式加密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesCfbEncrypt)]
|
||||
[[play](https://go.dev/play/p/tfkF10B13kH)]
|
||||
- **<big>AesCfbDecrypt</big>** : 使用AES CFB算法模式解密数据。
|
||||
- **<big>AesCfbDecrypt</big>** : 使用 AES CFB 算法模式解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesCfbDecrypt)]
|
||||
[[play](https://go.dev/play/p/tfkF10B13kH)]
|
||||
- **<big>AesOfbEncrypt</big>** : 使用AES OFB算法模式加密数据。
|
||||
- **<big>AesOfbEncrypt</big>** : 使用 AES OFB 算法模式加密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesOfbEncrypt)]
|
||||
[[play](https://go.dev/play/p/VtHxtkUj-3F)]
|
||||
- **<big>AesOfbDecrypt</big>** : 使用AES OFB算法模式解密数据。
|
||||
- **<big>AesOfbDecrypt</big>** : 使用 AES OFB 算法模式解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#AesOfbDecrypt)]
|
||||
[[play](https://go.dev/play/p/VtHxtkUj-3F)]
|
||||
- **<big>Base64StdEncode</big>** : 将字符串base64编码。
|
||||
- **<big>Base64StdEncode</big>** : 将字符串 base64 编码。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Base64StdEncode)]
|
||||
[[play](https://go.dev/play/p/VOaUyQUreoK)]
|
||||
- **<big>Base64StdDecode</big>** : 解码base64字符串。
|
||||
- **<big>Base64StdDecode</big>** : 解码 base64 字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Base64StdDecode)]
|
||||
[[play](https://go.dev/play/p/RWQylnJVgIe)]
|
||||
- **<big>DesEcbEncrypt</big>** : 使用DES ECB算法模式加密数据。
|
||||
- **<big>DesEcbEncrypt</big>** : 使用 DES ECB 算法模式加密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesEcbEncrypt)]
|
||||
[[play](https://go.dev/play/p/8qivmPeZy4P)]
|
||||
- **<big>DesEcbDecrypt</big>** : 使用DES ECB算法模解密数据。
|
||||
- **<big>DesEcbDecrypt</big>** : 使用 DES ECB 算法模解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesEcbDecrypt)]
|
||||
[[play](https://go.dev/play/p/8qivmPeZy4P)]
|
||||
- **<big>DesCbcEncrypt</big>** : 使用DES CBC算法模式加密数据。
|
||||
- **<big>DesCbcEncrypt</big>** : 使用 DES CBC 算法模式加密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesCbcEncrypt)]
|
||||
[[play](https://go.dev/play/p/4cC4QvWfe3_1)]
|
||||
- **<big>DesCbcDecrypt</big>** : 使用DES CBC算法模式解密数据。
|
||||
- **<big>DesCbcDecrypt</big>** : 使用 DES CBC 算法模式解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesCbcDecrypt)]
|
||||
[[play](https://go.dev/play/p/4cC4QvWfe3_1)]
|
||||
- **<big>DesCtrCrypt</big>** : 使用DES CTR算法模式加密/解密数据。
|
||||
- **<big>DesCtrCrypt</big>** : 使用 DES CTR 算法模式加密/解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesCtrCrypt)]
|
||||
[[play](https://go.dev/play/p/9-T6OjKpcdw)]
|
||||
- **<big>DesCfbEncrypt</big>** : 使用DES CFB算法模式加密数据。
|
||||
- **<big>DesCfbEncrypt</big>** : 使用 DES CFB 算法模式加密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesCfbEncrypt)]
|
||||
[[play](https://go.dev/play/p/y-eNxcFBlxL)]
|
||||
- **<big>DesCfbDecrypt</big>** : 使用DES CFB算法模式解密数据。
|
||||
- **<big>DesCfbDecrypt</big>** : 使用 DES CFB 算法模式解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesCfbDecrypt)]
|
||||
[[play](https://go.dev/play/p/y-eNxcFBlxL)]
|
||||
- **<big>DesOfbEncrypt</big>** : 使用DES OFB算法模式加密数据。
|
||||
- **<big>DesOfbEncrypt</big>** : 使用 DES OFB 算法模式加密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesOfbEncrypt)]
|
||||
[[play](https://go.dev/play/p/74KmNadjN1J)]
|
||||
- **<big>DesOfbDecrypt</big>** : 使用DES OFB算法模式解密数据。
|
||||
- **<big>DesOfbDecrypt</big>** : 使用 DES OFB 算法模式解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#DesOfbDecrypt)]
|
||||
[[play](https://go.dev/play/p/74KmNadjN1J)]
|
||||
- **<big>HmacMd5</big>** : 返回字符串md5 hmac值。
|
||||
- **<big>HmacMd5</big>** : 返回字符串 md5 hmac 值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#HmacMd5)]
|
||||
[[play](https://go.dev/play/p/uef0q1fz53I)]
|
||||
- **<big>HmacSha1</big>** : 返回字符串sha1 hmac值。
|
||||
- **<big>HmacSha1</big>** : 返回字符串 sha1 hmac 值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#HmacSha1)]
|
||||
[[play](https://go.dev/play/p/1UI4oQ4WXKM)]
|
||||
- **<big>HmacSha256</big>** : 返回字符串sha256 hmac值。
|
||||
- **<big>HmacSha256</big>** : 返回字符串 sha256 hmac 值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#HmacSha256)]
|
||||
[[play](https://go.dev/play/p/HhpwXxFhhC0)]
|
||||
- **<big>HmacSha512</big>** : 返回字符串sha256 hmac值。
|
||||
- **<big>HmacSha512</big>** : 返回字符串 sha256 hmac 值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#HmacSha512)]
|
||||
[[play](https://go.dev/play/p/59Od6m4A0Ud)]
|
||||
- **<big>Md5String</big>** : 返回字符串md5值。
|
||||
- **<big>Md5String</big>** : 返回字符串 md5 值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Md5String)]
|
||||
[[play](https://go.dev/play/p/1bLcVetbTOI)]
|
||||
- **<big>Md5File</big>** : 返回文件md5值。
|
||||
- **<big>Md5File</big>** : 返回文件 md5 值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Md5File)]
|
||||
- **<big>Sha1</big>** : 返回字符串sha1哈希值。
|
||||
- **<big>Sha1</big>** : 返回字符串 sha1 哈希值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Sha1)]
|
||||
[[play](https://go.dev/play/p/_m_uoD1deMT)]
|
||||
- **<big>Sha256</big>** :返回字符串sha256哈希值。
|
||||
- **<big>Sha256</big>** :返回字符串 sha256 哈希值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Sha256)]
|
||||
[[play](https://go.dev/play/p/tU9tfBMIAr1)]
|
||||
- **<big>Sha512</big>** : 返回字符串sha512哈希值。
|
||||
- **<big>Sha512</big>** : 返回字符串 sha512 哈希值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#Sha512)]
|
||||
[[play](https://go.dev/play/p/3WsvLYZxsHa)]
|
||||
- **<big>GenerateRsaKey</big>** : 在当前目录下创建rsa私钥文件和公钥文件。
|
||||
- **<big>GenerateRsaKey</big>** : 在当前目录下创建 rsa 私钥文件和公钥文件。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#GenerateRsaKey)]
|
||||
[[play](https://go.dev/play/p/zutRHrDqs0X)]
|
||||
- **<big>RsaEncrypt</big>** : 用公钥文件ras加密数据。
|
||||
- **<big>RsaEncrypt</big>** : 用公钥文件 ras 加密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#RsaEncrypt)]
|
||||
[[play](https://go.dev/play/p/uef0q1fz53I)]
|
||||
- **<big>RsaDecrypt</big>** : 用私钥文件rsa解密数据。
|
||||
- **<big>RsaDecrypt</big>** : 用私钥文件 rsa 解密数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#RsaDecrypt)]
|
||||
[[play](https://go.dev/play/p/uef0q1fz53I)]
|
||||
|
||||
|
||||
|
||||
### 6. datetime 日期时间处理包,格式化日期,比较日期。
|
||||
|
||||
@@ -426,32 +430,30 @@ import "github.com/duke-git/lancet/v2/datetime"
|
||||
- **<big>FormatStrToTime</big>** : 将字符串格式化成时间。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#FormatStrToTime)]
|
||||
[[play](https://go.dev/play/p/1h9FwdU8ql4)]
|
||||
- **<big>NewUnix</big>** : 创建一个unix时间戳。
|
||||
- **<big>NewUnix</big>** : 创建一个 unix 时间戳。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#NewUnix)]
|
||||
[[play](https://go.dev/play/p/psoSuh_kLRt)]
|
||||
- **<big>NewUnixNow</big>** : 创建一个当前时间的unix时间戳。
|
||||
- **<big>NewUnixNow</big>** : 创建一个当前时间的 unix 时间戳。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#NewUnixNow)]
|
||||
[[play](https://go.dev/play/p/U4PPx-9D0oz)]
|
||||
- **<big>NewFormat</big>** : 创建一个yyyy-mm-dd hh:mm:ss格式时间字符串的unix时间戳。
|
||||
- **<big>NewFormat</big>** : 创建一个 yyyy-mm-dd hh:mm:ss 格式时间字符串的 unix 时间戳。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#NewFormat)]
|
||||
[[play](https://go.dev/play/p/VkW08ZOaXPZ)]
|
||||
- **<big>NewISO8601</big>** : 创建一个iso8601格式时间字符串的unix时间戳。
|
||||
- **<big>NewISO8601</big>** : 创建一个 iso8601 格式时间字符串的 unix 时间戳。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#NewISO8601)]
|
||||
[[play](https://go.dev/play/p/mkhOHQkdeA2)]
|
||||
- **<big>ToUnix</big>** : 返回unix时间戳。
|
||||
- **<big>ToUnix</big>** : 返回 unix 时间戳。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#ToUnix)]
|
||||
[[play](https://go.dev/play/p/_LUiwAdocjy)]
|
||||
- **<big>ToFormat</big>** : 返回格式'yyyy-mm-dd hh:mm:ss'的日期字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#ToFormat)]
|
||||
[[play](https://go.dev/play/p/VkW08ZOaXPZ)]
|
||||
- **<big>ToFormatForTpl</big>** : 返回tpl格式指定的日期字符串。
|
||||
- **<big>ToFormatForTpl</big>** : 返回 tpl 格式指定的日期字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#ToFormatForTpl)]
|
||||
[[play](https://go.dev/play/p/nyXxXcQJ8L5)]
|
||||
- **<big>ToIso8601</big>** : 返回iso8601日期字符串。
|
||||
- **<big>ToIso8601</big>** : 返回 iso8601 日期字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#ToIso8601)]
|
||||
[[play](https://go.dev/play/p/mkhOHQkdeA2)]
|
||||
|
||||
|
||||
|
||||
### 7. datastructure 包含一些普通的数据结构实现。例如:list, linklist, stack, queue, set, tree, graph.
|
||||
|
||||
@@ -480,14 +482,11 @@ import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datastructure/set_zh-CN.md)]
|
||||
- **<big>Tree</big>** : 二叉搜索树。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datastructure/tree_zh-CN.md)]
|
||||
- **<big>Heap</big>** : 二叉max堆。
|
||||
- **<big>Heap</big>** : 二叉 max 堆。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datastructure/heap_zh-CN.md)]
|
||||
- **<big>Hashmap</big>** : 哈希映射。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datastructure/hashmap_zh-CN.md)]
|
||||
|
||||
|
||||
|
||||
|
||||
### 8. fileutil 包含文件基本操作。
|
||||
|
||||
```go
|
||||
@@ -562,35 +561,34 @@ import "github.com/duke-git/lancet/v2/function"
|
||||
|
||||
#### 函数列表:
|
||||
|
||||
- **<big>After</big>** : 创建一个函数,当该函数被调用n或更多次之后将执行传入的函数。
|
||||
- **<big>After</big>** : 创建一个函数,当该函数被调用 n 或更多次之后将执行传入的函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#After)]
|
||||
[[play](https://go.dev/play/p/eRD5k2vzUVX)]
|
||||
- **<big>Before</big>** : 创建一个函数,当该函数被调用不超过n次时,将执行执行传入的函数。
|
||||
- **<big>Before</big>** : 创建一个函数,当该函数被调用不超过 n 次时,将执行执行传入的函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Before)]
|
||||
[[play](https://go.dev/play/p/0HqUDIFZ3IL)]
|
||||
- **<big>CurryFn</big>** : 创建柯里化函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#CurryFn)]
|
||||
[[play](https://go.dev/play/p/5HopfDwANKX)]
|
||||
- **<big>Compose</big>** : 从右至左组合函数列表fnList,返回组合后的函数。
|
||||
- **<big>Compose</big>** : 从右至左组合函数列表 fnList,返回组合后的函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Compose)]
|
||||
[[play](https://go.dev/play/p/KKfugD4PKYF)]
|
||||
- **<big>Delay</big>** : 延迟delay时间后调用函数。
|
||||
- **<big>Delay</big>** : 延迟 delay 时间后调用函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Delay)]
|
||||
[[play](https://go.dev/play/p/Ivtc2ZE-Tye)]
|
||||
- **<big>Debounced</big>** : 创建一个debounced函数,该函数延迟调用fn直到自上次调用debounced函数后等待持续时间过去。
|
||||
- **<big>Debounced</big>** : 创建一个 debounced 函数,该函数延迟调用 fn 直到自上次调用 debounced 函数后等待持续时间过去。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Debounced)]
|
||||
[[play](https://go.dev/play/p/absuEGB_GN7)]
|
||||
- **<big>Schedule</big>** : 每次持续时间调用函数,直到关闭返回的channel。
|
||||
- **<big>Schedule</big>** : 每次持续时间调用函数,直到关闭返回的 channel。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Schedule)]
|
||||
[[play](https://go.dev/play/p/hbON-Xeyn5N)]
|
||||
- **<big>Pipeline</big>** : 从右至左执行函数列表。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Pipeline)]
|
||||
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
|
||||
- **<big>Watcher</big>** : Watcher用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。
|
||||
- **<big>Watcher</big>** : Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Watcher)]
|
||||
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
|
||||
|
||||
|
||||
### 11. maputil 包括一些操作 map 的函数.
|
||||
|
||||
```go
|
||||
@@ -605,12 +603,30 @@ import "github.com/duke-git/lancet/v2/maputil"
|
||||
- **<big>Filter</big>** : 迭代 map 中的每对 key 和 value,返回 map,其中的 key 和 value 符合 predicate 函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#Filter)]
|
||||
[[play](https://go.dev/play/p/fSvF3wxuNG7)]
|
||||
- **<big>FilterByKeys</big>** : 迭代 map, 返回一个新 map,其 key 都是给定的 key 值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FilterByKeys)]
|
||||
[[play](https://go.dev/play/p/7ov6BJHbVqh)]
|
||||
- **<big>FilterByValues</big>** : 迭代 map, 返回一个新 map,其 value 都是给定的 value 值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FilterByValues)]
|
||||
[[play](https://go.dev/play/p/P3-9MdcXegR)]
|
||||
- **<big>OmitBy</big>** : Filter 的反向操作, 迭代 map 中的每对 key 和 value, 删除符合 predicate 函数的 key, value, 返回新 map。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#OmitBy)]
|
||||
[[play](https://go.dev/play/p/YJM4Hj5hNwm)]
|
||||
- **<big>OmitByKeys</big>** : FilterByKeys 的反向操作, 迭代 map, 返回一个新 map,其 key 不包括给定的 key 值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#OmitByKeys)]
|
||||
[[play](https://go.dev/play/p/jXGrWDBfSRp)]
|
||||
- **<big>OmitByValues</big>** : FilterByValues 的反向操作, 迭代 map, 返回一个新 map,其 value 不包括给定的 value 值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#OmitByValues)]
|
||||
[[play](https://go.dev/play/p/XB7Y10uw20_U)]
|
||||
- **<big>Intersect</big>** : 多个 map 的交集操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#Intersect)]
|
||||
[[play](https://go.dev/play/p/Zld0oj3sjcC)]
|
||||
- **<big>Keys</big>** : 返回 map 中所有 key 组成的切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#Keys)]
|
||||
[[play](https://go.dev/play/p/xNB5bTb97Wd)]
|
||||
- **<big>KeysBy</big>** : 创建一个切片,其元素是每个 map 的 key 调用 mapper 函数的结果。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#KeysBy)]
|
||||
[[play](https://go.dev/play/p/hI371iB8Up8)]
|
||||
- **<big>Merge</big>** : 合并多个 map, 相同的 key 会被之后的 key 覆盖。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#Merge)]
|
||||
[[play](https://go.dev/play/p/H95LENF1uB-)]
|
||||
@@ -620,6 +636,24 @@ import "github.com/duke-git/lancet/v2/maputil"
|
||||
- **<big>Values</big>** : 返回 map 中所有 values 组成的切片
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#Values)]
|
||||
[[play](https://go.dev/play/p/CBKdUc5FTW6)]
|
||||
- **<big>ValuesBy</big>** : 创建一个切片,其元素是每个 map 的 value 调用 mapper 函数的结果。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#ValuesBy)]
|
||||
[[play](https://go.dev/play/p/sg9-oRidh8f)]
|
||||
- **<big>MapKeys</big>** : 操作 map 的每个 key,然后转为新的 map。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#MapKeys)]
|
||||
[[play](https://go.dev/play/p/8scDxWeBDKd)]
|
||||
- **<big>MapValues</big>** : 操作 map 的每个 value,然后转为新的 map。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#MapValues)]
|
||||
[[play](https://go.dev/play/p/g92aY3fc7Iw)]
|
||||
- **<big>Entries</big>** : 将 map 转换为键/值对切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#Entries)]
|
||||
[[play](https://go.dev/play/p/Ltb11LNcElY)]
|
||||
- **<big>FromEntries</big>** : 基于键/值对的切片创建 map。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FromEntries)]
|
||||
[[play](https://go.dev/play/p/fTdu4sCNjQO)]
|
||||
- **<big>Transform</big>** : 将 map 转换为其他类型的 map。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#Transform)]
|
||||
[[play](https://go.dev/play/p/P6ovfToM3zj)]
|
||||
- **<big>IsDisjoint</big>** : 验证两个 map 是否具有不同的 key。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#IsDisjoint)]
|
||||
[[play](https://go.dev/play/p/N9qgYg_Ho6f)]
|
||||
@@ -668,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 请求。
|
||||
|
||||
@@ -677,64 +725,61 @@ import "github.com/duke-git/lancet/v2/netutil"
|
||||
|
||||
#### 函数列表:
|
||||
|
||||
- **<big>ConvertMapToQueryString</big>** : 将map转换成http查询字符串。
|
||||
- **<big>ConvertMapToQueryString</big>** : 将 map 转换成 http 查询字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#ConvertMapToQueryString)]
|
||||
[[play](https://go.dev/play/p/jnNt_qoSnRi)]
|
||||
- **<big>EncodeUrl</big>** : 编码url query string的值(?a=1&b=[2] -> ?a=1&b=%5B2%5D)。
|
||||
- **<big>EncodeUrl</big>** : 编码 url query string 的值(?a=1&b=[2] -> ?a=1&b=%5B2%5D)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#EncodeUrl)]
|
||||
[[play](https://go.dev/play/p/bsZ6BRC4uKI)]
|
||||
- **<big>GetInternalIp</big>** : 获取内部ipv4。
|
||||
- **<big>GetInternalIp</big>** : 获取内部 ipv4。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#GetInternalIp)]
|
||||
[[play](https://go.dev/play/p/5mbu-gFp7ei)]
|
||||
- **<big>GetIps</big>** : 获取系统ipv4地址列表。
|
||||
- **<big>GetIps</big>** : 获取系统 ipv4 地址列表。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#GetIps)]
|
||||
[[play](https://go.dev/play/p/NUFfcEmukx1)]
|
||||
- **<big>GetMacAddrs</big>** : 获取系统mac地址列。
|
||||
- **<big>GetMacAddrs</big>** : 获取系统 mac 地址列。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#GetMacAddrs)]
|
||||
[[play](https://go.dev/play/p/Rq9UUBS_Xp1)]
|
||||
- **<big>GetPublicIpInfo</big>** : 获取[公网ip信息](http://ip-api.com/json/).
|
||||
- **<big>GetPublicIpInfo</big>** : 获取[公网 ip 信息](http://ip-api.com/json/).
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#GetPublicIpInfo)]
|
||||
[[play](https://go.dev/play/p/YDxIfozsRHR)]
|
||||
- **<big>GetRequestPublicIp</big>** : 获取http请求ip。
|
||||
- **<big>GetRequestPublicIp</big>** : 获取 http 请求 ip。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#GetRequestPublicIp)]
|
||||
[[play](https://go.dev/play/p/kxU-YDc_eBo)]
|
||||
- **<big>IsPublicIP</big>** : 判断ip是否是公共ip。
|
||||
- **<big>IsPublicIP</big>** : 判断 ip 是否是公共 ip。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#IsPublicIP)]
|
||||
[[play](https://go.dev/play/p/nmktSQpJZnn)]
|
||||
- **<big>IsInternalIP</big>** : 判断ip是否是局域网ip。
|
||||
- **<big>IsInternalIP</big>** : 判断 ip 是否是局域网 ip。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#IsInternalIP)]
|
||||
[[play](https://go.dev/play/p/sYGhXbgO4Cb)]
|
||||
- **<big>HttpRequest</big>** : 用于抽象HTTP请求实体的结构。
|
||||
- **<big>HttpRequest</big>** : 用于抽象 HTTP 请求实体的结构。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpRequest)]
|
||||
[[play](https://go.dev/play/p/jUSgynekH7G)]
|
||||
- **<big>HttpClient</big>** : 用于发送HTTP请求。
|
||||
- **<big>HttpClient</big>** : 用于发送 HTTP 请求。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpClient)]
|
||||
[[play](https://go.dev/play/p/jUSgynekH7G)]
|
||||
- **<big>SendRequest</big>** : 发送http请求。
|
||||
- **<big>SendRequest</big>** : 发送 http 请求。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#SendRequest)]
|
||||
[[play](https://go.dev/play/p/jUSgynekH7G)]
|
||||
- **<big>DecodeResponse</big>** : 解析http响应体到目标结构体。
|
||||
- **<big>DecodeResponse</big>** : 解析 http 响应体到目标结构体。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#DecodeResponse)]
|
||||
[[play](https://go.dev/play/p/jUSgynekH7G)]
|
||||
- **<big>StructToUrlValues</big>** : 将结构体转为url values, 仅转化结构体导出字段并且包含`json` tag。
|
||||
- **<big>StructToUrlValues</big>** : 将结构体转为 url values, 仅转化结构体导出字段并且包含`json` tag。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#StructToUrlValues)]
|
||||
[[play](https://go.dev/play/p/pFqMkM40w9z)]
|
||||
- **<big>HttpGet<sup>deprecated</sup></big>** : 发送http get请求(已弃用:SendRequest代替)。
|
||||
- **<big>HttpGet<sup>deprecated</sup></big>** : 发送 http get 请求(已弃用:SendRequest 代替)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpGet)]
|
||||
- **<big>HttpDelete<sup>deprecated</sup></big>** : 发送http delete请求(已弃用:SendRequest代替)。
|
||||
- **<big>HttpDelete<sup>deprecated</sup></big>** : 发送 http delete 请求(已弃用:SendRequest 代替)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpDelete)]
|
||||
- **<big>HttpPost<sup>deprecated</sup></big>** : 发送http post请求(已弃用:SendRequest代替)。
|
||||
- **<big>HttpPost<sup>deprecated</sup></big>** : 发送 http post 请求(已弃用:SendRequest 代替)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpPost)]
|
||||
- **<big>HttpPut<sup>deprecated</sup></big>** : 发送http put请求(已弃用:SendRequest代替)。
|
||||
- **<big>HttpPut<sup>deprecated</sup></big>** : 发送 http put 请求(已弃用:SendRequest 代替)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpPut)]
|
||||
- **<big>HttpPatch<sup>deprecated</sup></big>** : 发送http patch请求(已弃用:SendRequest代替)。
|
||||
- **<big>HttpPatch<sup>deprecated</sup></big>** : 发送 http patch 请求(已弃用:SendRequest 代替)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#HttpPatch)]
|
||||
- **<big>ParseHttpResponse</big>** : 解析http响应体到目标结构体。
|
||||
- **<big>ParseHttpResponse</big>** : 解析 http 响应体到目标结构体。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#ParseHttpResponse)]
|
||||
|
||||
|
||||
|
||||
|
||||
### 14. random 随机数生成器包,可以生成随机[]bytes, int, string。
|
||||
|
||||
```go
|
||||
@@ -746,7 +791,7 @@ import "github.com/duke-git/lancet/v2/random"
|
||||
- **<big>RandBytes</big>** : 生成随机字节切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/random_zh-CN.md#RandBytes)]
|
||||
[[play](https://go.dev/play/p/EkiLESeXf8d)]
|
||||
- **<big>RandInt</big>** : 生成随机int, 范围[min, max)。
|
||||
- **<big>RandInt</big>** : 生成随机 int, 范围[min, max)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/random_zh-CN.md#RandInt)]
|
||||
[[play](https://go.dev/play/p/pXyyAAI5YxD)]
|
||||
- **<big>RandString</big>** : 生成给定长度的随机字符串,只包含字母(a-zA-Z)。
|
||||
@@ -764,11 +809,9 @@ import "github.com/duke-git/lancet/v2/random"
|
||||
- **<big>RandNumeralOrLetter</big>** : 生成给定长度的随机字符串(数字+字母)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/random_zh-CN.md#RandNumeralOrLetter)]
|
||||
[[play](https://go.dev/play/p/19CEQvpx2jD)]
|
||||
- **<big>UUIdV4</big>** : 生成UUID v4字符串。
|
||||
- **<big>UUIdV4</big>** : 生成 UUID v4 字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/random_zh-CN.md#UUIdV4)]
|
||||
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
|
||||
|
||||
|
||||
|
||||
### 15. retry 重试执行函数直到函数运行成功或被 context cancel。
|
||||
|
||||
@@ -778,24 +821,22 @@ import "github.com/duke-git/lancet/v2/retry"
|
||||
|
||||
#### 函数列表:
|
||||
|
||||
- **<big>Context</big>** : 设置重试context参数。
|
||||
- **<big>Context</big>** : 设置重试 context 参数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#Context)]
|
||||
[[play](https://go.dev/play/p/xnAOOXv9GkS)]
|
||||
- **<big>Retry</big>** : 重试执行函数retryFunc,直到函数运行成功,或被context取消。
|
||||
- **<big>Retry</big>** : 重试执行函数 retryFunc,直到函数运行成功,或被 context 取消。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#Retry)]
|
||||
[[play](https://go.dev/play/p/nk2XRmagfVF)]
|
||||
- **<big>RetryFunc</big>** : 重试执行的函数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#RetryFunc)]
|
||||
[[play](https://go.dev/play/p/nk2XRmagfVF)]
|
||||
- **<big>RetryDuration</big>** : 设置重试间隔时间,默认3秒。
|
||||
- **<big>RetryDuration</big>** : 设置重试间隔时间,默认 3 秒。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#RetryDuration)]
|
||||
[[play](https://go.dev/play/p/nk2XRmagfVF)]
|
||||
- **<big>RetryTimes</big>** : 设置重试次数,默认5。
|
||||
- **<big>RetryTimes</big>** : 设置重试次数,默认 5。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#RetryTimes)]
|
||||
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
|
||||
|
||||
|
||||
|
||||
### 16. slice 包含操作切片的方法集合。
|
||||
|
||||
```go
|
||||
@@ -803,34 +844,38 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
```
|
||||
|
||||
#### 函数列表:
|
||||
|
||||
- **<big>AppendIfAbsent</big>** : 当前切片中不包含值时,将该值追加到切片中。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#AppendIfAbsent)]
|
||||
[[play](https://go.dev/play/p/GNdv7Jg2Taj)]
|
||||
- **<big>Contain</big>** : 判断slice是否包含value。
|
||||
- **<big>Contain</big>** : 判断 slice 是否包含 value。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Contain)]
|
||||
[[play](https://go.dev/play/p/_454yEHcNjf)]
|
||||
- **<big>ContainSubSlice</big>** : 判断slice是否包含subslice。
|
||||
- **<big>ContainBy</big>** : 根据 predicate 函数判断切片是否包含某个值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ContainBy)]
|
||||
[[play](https://go.dev/play/p/49tkHfX4GNc)]
|
||||
- **<big>ContainSubSlice</big>** : 判断 slice 是否包含 subslice。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ContainSubSlice)]
|
||||
[[play](https://go.dev/play/p/bcuQ3UT6Sev)]
|
||||
- **<big>Chunk</big>** : 按照size参数均分slice。
|
||||
- **<big>Chunk</big>** : 按照 size 参数均分 slice。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Chunk)]
|
||||
[[play](https://go.dev/play/p/b4Pou5j2L_C)]
|
||||
- **<big>Compact</big>** : 去除slice中的假值(false values are false, nil, 0, "")。
|
||||
- **<big>Compact</big>** : 去除 slice 中的假值(false values are false, nil, 0, "")。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Compact)]
|
||||
[[play](https://go.dev/play/p/pO5AnxEr3TK)]
|
||||
- **<big>Concat</big>** : 合并多个slices到一个slice中。
|
||||
- **<big>Concat</big>** : 合并多个 slices 到一个 slice 中。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Concat)]
|
||||
[[play](https://go.dev/play/p/gPt-q7zr5mk)]
|
||||
- **<big>Count</big>** : 返回切片中指定元素的个数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Count)]
|
||||
[[play](https://go.dev/play/p/Mj4oiEnQvRJ)]
|
||||
- **<big>CountBy</big>** : 遍历切片,对每个元素执行函数predicate. 返回符合函数返回值为true的元素的个数。
|
||||
- **<big>CountBy</big>** : 遍历切片,对每个元素执行函数 predicate. 返回符合函数返回值为 true 的元素的个数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#CountBy)]
|
||||
[[play](https://go.dev/play/p/tHOccTMDZCC)]
|
||||
- **<big>Difference</big>** : 创建一个切片,其元素不包含在另一个给定切片中。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Difference)]
|
||||
[[play](https://go.dev/play/p/VXvadzLzhDa)]
|
||||
- **<big>DifferenceBy</big>** : 将两个slice中的每个元素调用iteratee函数,并比较它们的返回值,如果不相等返回在slice中对应的值。
|
||||
- **<big>DifferenceBy</big>** : 将两个 slice 中的每个元素调用 iteratee 函数,并比较它们的返回值,如果不相等返回在 slice 中对应的值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#DifferenceBy)]
|
||||
[[play](https://go.dev/play/p/DiivgwM5OnC)]
|
||||
- **<big>DifferenceWith</big>** : 接受比较器函数,该比较器被调用以将切片的元素与值进行比较。 结果值的顺序和引用由第一个切片确定。
|
||||
@@ -839,25 +884,37 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>DeleteAt</big>** : 删除切片中指定开始索引到结束索引的元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#DeleteAt)]
|
||||
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
|
||||
- **<big>Drop</big>** : 创建一个切片,当n > 0时从开头删除n个元素,或者当n < 0时从结尾删除n个元素。
|
||||
- **<big>Drop</big>** : 从切片头部删除 n 个元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Drop)]
|
||||
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
|
||||
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
|
||||
- **<big>DropRight</big>** : 从切片尾部删除 n 个元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#DropRight)]
|
||||
[[play](https://go.dev/play/p/8bcXvywZezG)]
|
||||
- **<big>DropWhile</big>** : 从切片的头部删除 n 个元素,这个 n 个元素满足 predicate 函数返回 true。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#DropWhile)]
|
||||
[[play](https://go.dev/play/p/4rt252UV_qs)]
|
||||
- **<big>DropRightWhile</big>** : 从切片的尾部删除 n 个元素,这个 n 个元素满足 predicate 函数返回 true。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#DropRightWhile)]
|
||||
[[play](https://go.dev/play/p/6wyK3zMY56e)]
|
||||
- **<big>Equal</big>** : 检查两个切片是否相等,相等条件:切片长度相同,元素顺序和值都相同。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Equal)]
|
||||
[[play](https://go.dev/play/p/WcRQJ37ifPa)]
|
||||
- **<big>EqualWith</big>** : 检查两个切片是否相等,相等条件:对两个切片的元素调用比较函数comparator,返回true。
|
||||
- **<big>EqualWith</big>** : 检查两个切片是否相等,相等条件:对两个切片的元素调用比较函数 comparator,返回 true。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#EqualWith)]
|
||||
[[play](https://go.dev/play/p/b9iygtgsHI1)]
|
||||
- **<big>Every</big>** : 如果切片中的所有值都通过谓词函数,则返回true。
|
||||
- **<big>Every</big>** : 如果切片中的所有值都通过谓词函数,则返回 true。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Every)]
|
||||
[[play](https://go.dev/play/p/R8U6Sl-j8cD)]
|
||||
- **<big>Filter</big>** : 返回切片中通过predicate函数真值测试的所有元素。
|
||||
- **<big>Filter</big>** : 返回切片中通过 predicate 函数真值测试的所有元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Filter)]
|
||||
[[play](https://go.dev/play/p/SdPna-7qK4T)]
|
||||
- **<big>Find</big>** : 遍历切片的元素,返回第一个通过predicate函数真值测试的元素。
|
||||
- **<big>FilterMap</big>** : 返回一个将 filter 和 map 操作应用于给定切片的切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FilterMap)]
|
||||
[[play](https://go.dev/play/p/J94SZ_9MiIe)]
|
||||
- **<big>Find</big>** : 遍历切片的元素,返回第一个通过 predicate 函数真值测试的元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Find)]
|
||||
[[play](https://go.dev/play/p/CBKeBoHVLgq)]
|
||||
- **<big>FindLast</big>** : 从头到尾遍历slice的元素,返回最后一个通过predicate函数真值测试的元素。
|
||||
- **<big>FindLast</big>** : 从头到尾遍历 slice 的元素,返回最后一个通过 predicate 函数真值测试的元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FindLast)]
|
||||
[[play](https://go.dev/play/p/FFDPV_j7URd)]
|
||||
- **<big>Flatten</big>** : 将多维切片展平一层。
|
||||
@@ -866,19 +923,25 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>FlattenDeep</big>** : 将多维切片递归展平到一层。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FlattenDeep)]
|
||||
[[play](https://go.dev/play/p/yjYNHPyCFaF)]
|
||||
- **<big>ForEach</big>** : 遍历切片的元素并为每个元素调用iteratee函数。
|
||||
- **<big>FlatMap</big>** : 将切片转换为其它类型切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FlatMap)]
|
||||
[[play](https://go.dev/play/p/_QARWlWs1N_F)]
|
||||
- **<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)]
|
||||
- **<big>GroupWith</big>** : 创建一个map,key是iteratee遍历slice中的每个元素返回的结果。值是切片元素。
|
||||
- **<big>GroupWith</big>** : 创建一个 map,key 是 iteratee 遍历 slice 中的每个元素返回的结果。值是切片元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#GroupWith)]
|
||||
[[play](https://go.dev/play/p/ApCvMNTLO8a)]
|
||||
- **<big>IntSlice<sup>deprecated</sup></big>** : 将接口切片转换为int切片。
|
||||
- **<big>IntSlice<sup>deprecated</sup></big>** : 将接口切片转换为 int 切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#IntSlice)]
|
||||
[[play](https://go.dev/play/p/FdQXF0Vvqs-)]
|
||||
- **<big>InterfaceSlice<sup>deprecated</sup></big>** : 将值转换为interface切片。
|
||||
- **<big>InterfaceSlice<sup>deprecated</sup></big>** : 将值转换为 interface 切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#InterfaceSlice)]
|
||||
[[play](https://go.dev/play/p/FdQXF0Vvqs-)]
|
||||
- **<big>Intersection</big>** : 返回多个切片的交集。
|
||||
@@ -893,7 +956,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>LastIndexOf</big>** : 返回在切片中找到最后一个值的索引,如果找不到该值,则返回-1。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#LastIndexOf)]
|
||||
[[play](https://go.dev/play/p/DokM4cf1IKH)]
|
||||
- **<big>Map</big>** : 对slice中的每个元素执行map函数以创建一个新切片。
|
||||
- **<big>Map</big>** : 对 slice 中的每个元素执行 map 函数以创建一个新切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Map)]
|
||||
[[play](https://go.dev/play/p/biaTefqPquw)]
|
||||
- **<big>Merge</big>** : 合并多个切片(不会消除重复元素)。
|
||||
@@ -902,31 +965,43 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Reverse</big>** : 反转切片中的元素顺序。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Reverse)]
|
||||
[[play](https://go.dev/play/p/8uI8f1lwNrQ)]
|
||||
- **<big>Reduce</big>** : 将切片中的元素依次运行iteratee函数,返回运行结果。
|
||||
- **<big>Reduce</big>** : 将切片中的元素依次运行 iteratee 函数,返回运行结果。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Reduce)]
|
||||
[[play](https://go.dev/play/p/_RfXJJWIsIm)]
|
||||
- **<big>Replace</big>** : 返回切片的副本,其中前n个不重叠的old替换为new。
|
||||
- **<big>Replace</big>** : 返回切片的副本,其中前 n 个不重叠的 old 替换为 new。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Replace)]
|
||||
[[play](https://go.dev/play/p/P5mZp7IhOFo)]
|
||||
- **<big>ReplaceAll</big>** : 返回切片的副本,将其中old全部替换为new。
|
||||
- **<big>ReplaceAll</big>** : 返回切片的副本,将其中 old 全部替换为 new。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ReplaceAll)]
|
||||
[[play](https://go.dev/play/p/CzqXMsuYUrx)]
|
||||
- **<big>Repeat</big>** : 创建一个切片,包含n个传入的item。
|
||||
- **<big>Repeat</big>** : 创建一个切片,包含 n 个传入的 item。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Repeat)]
|
||||
[[play](https://go.dev/play/p/1CbOmtgILUU)]
|
||||
- **<big>Shuffle</big>** : 随机打乱切片中的元素顺序。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Shuffle)]
|
||||
[[play](https://go.dev/play/p/YHvhnWGU3Ge)]
|
||||
- **<big>IsAscending</big>** : 检查切片元素是否按升序排列。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#IsAscending)]
|
||||
[[play](https://go.dev/play/p/9CtsFjet4SH)]
|
||||
- **<big>IsDescending</big>** : 检查切片元素是否按降序排列。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#IsDescending)]
|
||||
[[play](https://go.dev/play/p/U_LljFXma14)]
|
||||
- **<big>IsSorted</big>** : 检查切片元素是否是有序的(升序或降序)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#IsSorted)]
|
||||
[[play](https://go.dev/play/p/nCE8wPLwSA-)]
|
||||
- **<big>IsSortedByKey</big>** : 通过 iteratee 函数,检查切片元素是否是有序的。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#IsSortedByKey)]
|
||||
[[play](https://go.dev/play/p/tUoGB7DOHI4)]
|
||||
- **<big>Sort</big>** : 对任何有序类型(数字或字符串)的切片进行排序,使用快速排序算法。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Sort)]
|
||||
[[play](https://go.dev/play/p/V9AVjzf_4Fk)]
|
||||
- **<big>SortBy</big>** : 按照less函数确定的升序规则对切片进行排序。排序不保证稳定性。
|
||||
- **<big>SortBy</big>** : 按照 less 函数确定的升序规则对切片进行排序。排序不保证稳定性。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#SortBy)]
|
||||
[[play](https://go.dev/play/p/DAhLQSZEumm)]
|
||||
- **<big>SortByField<sup>deprecated</sup></big>** : 按字段对结构切片进行排序。slice元素应为struct,字段类型应为int、uint、string或bool。
|
||||
- **<big>SortByField<sup>deprecated</sup></big>** : 按字段对结构切片进行排序。slice 元素应为 struct,字段类型应为 int、uint、string 或 bool。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#SortByField)]
|
||||
[[play](https://go.dev/play/p/fU1prOBP9p1)]
|
||||
- **<big>Some</big>** : 如果列表中的任何值通过谓词函数,则返回true。
|
||||
- **<big>Some</big>** : 如果列表中的任何值通过谓词函数,则返回 true。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Some)]
|
||||
[[play](https://go.dev/play/p/4pO9Xf9NDGS)]
|
||||
- **<big>StringSlice<sup>deprecated</sup></big>** : 将接口切片转换为字符串切片。
|
||||
@@ -944,7 +1019,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Unique</big>** : 删除切片中的重复元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Unique)]
|
||||
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
|
||||
- **<big>UniqueBy</big>** : 对切片的每个元素调用iteratee函数,然后删除重复元素。
|
||||
- **<big>UniqueBy</big>** : 对切片的每个元素调用 iteratee 函数,然后删除重复元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#UniqueBy)]
|
||||
[[play](https://go.dev/play/p/UR323iZLDpv)]
|
||||
- **<big>Union</big>** : 合并多个切片。
|
||||
@@ -959,12 +1034,46 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Without</big>** : 创建一个不包括所有给定值的切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Without)]
|
||||
[[play](https://go.dev/play/p/bwhEXEypThg)]
|
||||
- **<big>KeyBy</big>** :将切片每个元素调用函数后转为map。
|
||||
- **<big>KeyBy</big>** :将切片每个元素调用函数后转为 map。
|
||||
[[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"
|
||||
@@ -1005,6 +1114,9 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- **<big>UpperFirst</big>** : 将字符串的第一个字符转换为大写形式。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#UpperFirst)]
|
||||
[[play](https://go.dev/play/p/sBbBxRbs8MM)]
|
||||
- **<big>Pad</big>** : 如果字符串长度短于 size,则在左右两侧填充字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Pad)]
|
||||
[[play](https://go.dev/play/p/NzImQq-VF8q)]
|
||||
- **<big>PadEnd</big>** : 如果字符串短于限制大小,则在右侧用给定字符填充字符串。 如果填充字符超出大小,它们将被截断。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadEnd)]
|
||||
[[play](https://go.dev/play/p/9xP8rN0vz--)]
|
||||
@@ -1032,8 +1144,16 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- **<big>Unwrap</big>** : 从另一个字符串中解开一个给定的字符串。 将更改源字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Unwrap)]
|
||||
[[play](https://go.dev/play/p/Ec2q4BzCpG-)]
|
||||
- **<big>SplitWords</big>** : 将字符串拆分为单词,只支持字母字符单词。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#SplitWords)]
|
||||
[[play](https://go.dev/play/p/KLiX4WiysMM)]
|
||||
- **<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"
|
||||
@@ -1069,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"
|
||||
@@ -1098,7 +1218,7 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsAllLower</big>** : 验证字符串是否全是小写英文字母。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsAllLower)]
|
||||
[[play](https://go.dev/play/p/GjqCnOfV6cM)]
|
||||
- **<big>IsBase64</big>** : 验证字符串是否是base64编码。
|
||||
- **<big>IsBase64</big>** : 验证字符串是否是 base64 编码。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsBase64)]
|
||||
[[play](https://go.dev/play/p/sWHEySAt6hl)]
|
||||
- **<big>IsChineseMobile</big>** : 验证字符串是否是中国手机号码。
|
||||
@@ -1113,7 +1233,7 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsCreditCard</big>** : 验证字符串是否是信用卡号码。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsCreditCard)]
|
||||
[[play](https://go.dev/play/p/sNwwL6B0-v4)]
|
||||
- **<big>IsDns</big>** : 验证字符串是否是有效dns。
|
||||
- **<big>IsDns</big>** : 验证字符串是否是有效 dns。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsDns)]
|
||||
[[play](https://go.dev/play/p/jlYApVLLGTZ)]
|
||||
- **<big>IsEmail</big>** : 验证字符串是否是有效电子邮件地址。
|
||||
@@ -1128,7 +1248,7 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsNumberStr</big>** : 验证字符串是否是可以转换为数字。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsNumberStr)]
|
||||
[[play](https://go.dev/play/p/LzaKocSV79u)]
|
||||
- **<big>IsJSON</big>** : 验证字符串是否是有效json。
|
||||
- **<big>IsJSON</big>** : 验证字符串是否是有效 json。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsJSON)]
|
||||
[[play](https://go.dev/play/p/sRS6c4K8jGk)]
|
||||
- **<big>IsRegexMatch</big>** : 验证字符串是否可以匹配正则表达式。
|
||||
@@ -1137,19 +1257,19 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsIntStr</big>** : 验证字符串是否是可以转换为整数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsIntStr)]
|
||||
[[play](https://go.dev/play/p/jQRtFv-a0Rk)]
|
||||
- **<big>IsIp</big>** : 验证字符串是否是ip地址。
|
||||
- **<big>IsIp</big>** : 验证字符串是否是 ip 地址。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsIp)]
|
||||
[[play](https://go.dev/play/p/FgcplDvmxoD)]
|
||||
- **<big>IsIpV4</big>** : 验证字符串是否是ipv4地址。
|
||||
- **<big>IsIpV4</big>** : 验证字符串是否是 ipv4 地址。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsIpV4)]
|
||||
[[play](https://go.dev/play/p/zBGT99EjaIu)]
|
||||
- **<big>IsIpV6</big>** : 验证字符串是否是ipv6地址。
|
||||
- **<big>IsIpV6</big>** : 验证字符串是否是 ipv6 地址。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsIpV6)]
|
||||
[[play](https://go.dev/play/p/AHA0r0AzIdC)]
|
||||
- **<big>IsStrongPassword</big>** : 验证字符串是否是强密码:(字母+数字+特殊字符)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsStrongPassword)]
|
||||
[[play](https://go.dev/play/p/QHdVcSQ3uDg)]
|
||||
- **<big>IsUrl</big>** : 验证字符串是否是url。
|
||||
- **<big>IsUrl</big>** : 验证字符串是否是 url。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsUrl)]
|
||||
[[play](https://go.dev/play/p/pbJGa7F98Ka)]
|
||||
- **<big>IsWeakPassword</big>** : 验证字符串是否是弱密码(只包含字母+数字)。
|
||||
@@ -1158,12 +1278,15 @@ import "github.com/duke-git/lancet/v2/validator"
|
||||
- **<big>IsZeroValue</big>** : 判断传入的参数值是否为零值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsZeroValue)]
|
||||
[[play](https://go.dev/play/p/UMrwaDCi_t4)]
|
||||
- **<big>IsGBK</big>** : 检查数据编码是否为gbk(汉字内部代码扩展规范)。
|
||||
- **<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"
|
||||
@@ -1171,11 +1294,45 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
|
||||
#### 函数列表:
|
||||
|
||||
|
||||
- **<big>Unwrap</big>** : 检查error, 如果err为nil则展开,则它返回一个有效值,如果err不是nil则Unwrap使用err发生panic。
|
||||
- **<big>New</big>** : 创建 XError 对象实例。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#New)]
|
||||
[[play](https://go.dev/play/p/w4oWZts7q7f)]
|
||||
- **<big>Wrap</big>** : 根据 error 对象创建 XError 对象实例,可添加 message。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#Wrap)]
|
||||
[[play](https://go.dev/play/p/5385qT2dCi4)]
|
||||
- **<big>Unwrap</big>** : 从 error 对象中解构出 XError。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#Unwrap)]
|
||||
[[play](https://go.dev/play/p/w84d7Mb3Afk)]
|
||||
|
||||
[[play](https://go.dev/play/p/LKMLep723tu)]
|
||||
- **<big>XError_Wrap</big>** : 创建新的 XError 对象并将消息和 id 复制到新的对象中。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#XError_Wrap)]
|
||||
[[play](https://go.dev/play/p/5385qT2dCi4)]
|
||||
- **<big>XError_Unwrap</big>** : 解构 XEerror 为 error 对象。适配 github.com/pkg/errors。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#XError_Unwrap)]
|
||||
[[play](https://go.dev/play/p/VUXJ8BST4c6)]
|
||||
- **<big>XError_With</big>** : 添加与 XError 对象的键和值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#XError_With)]
|
||||
[[play](https://go.dev/play/p/ow8UISXX_Dp)]
|
||||
- **<big>XError_Id</big>** : 设置 XError 对象的 id。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#XError_Id)]
|
||||
[[play](https://go.dev/play/p/X6HBlsy58U9)]
|
||||
- **<big>XError_Is</big>** : 检查目标 error 是否为 XError,两个错误中的 error.id 是否匹配。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#XError_Is)]
|
||||
[[play](https://go.dev/play/p/X6HBlsy58U9)]
|
||||
- **<big>XError_Values</big>** : 返回由 With 设置的键和值的映射。将合并所有 XError 键和值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#XError_Values)]
|
||||
[[play](https://go.dev/play/p/ow8UISXX_Dp)]
|
||||
- **<big>XError_StackTrace</big>** : 返回与 pkg/error 兼容的堆栈信息。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#XError_StackTrace)]
|
||||
[[play](https://go.dev/play/p/6FAvSQpa7pc)]
|
||||
- **<big>XError_Info</big>** : 返回可打印的 XError 对象信息。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#XError_Info)]
|
||||
[[play](https://go.dev/play/p/1ZX0ME1F-Jb)]
|
||||
- **<big>XError_Error</big>** : 实现标准库的 error 接口。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#XError_Error)]
|
||||
[[play](https://go.dev/play/p/w4oWZts7q7f)]
|
||||
- **<big>TryUnwrap</big>** : 检查 error, 如果 err 为 nil 则展开,则它返回一个有效值,如果 err 不是 nil 则 Unwrap 使用 err 发生 panic。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#TryUnwrap)]
|
||||
[[play](https://go.dev/play/p/acyZVkNZEeW)]
|
||||
|
||||
## 如何贡献代码
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -9,10 +9,11 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/structs"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -234,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.
|
||||
@@ -324,3 +301,62 @@ func DecodeByte(data []byte, target any) error {
|
||||
decoder := gob.NewDecoder(buffer)
|
||||
return decoder.Decode(target)
|
||||
}
|
||||
|
||||
// DeepClone creates a deep copy of passed item.
|
||||
// can't clone unexported field of struct
|
||||
// Play: https://go.dev/play/p/j4DP5dquxnk
|
||||
func DeepClone[T any](src T) T {
|
||||
c := cloner{
|
||||
ptrs: map[reflect.Type]map[uintptr]reflect.Value{},
|
||||
}
|
||||
result := c.clone(reflect.ValueOf(src))
|
||||
if result.Kind() == reflect.Invalid {
|
||||
var zeroValue T
|
||||
return zeroValue
|
||||
}
|
||||
|
||||
return result.Interface().(T)
|
||||
}
|
||||
|
||||
// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers.
|
||||
// Play: https://go.dev/play/p/FOVY3XJL-6B
|
||||
func CopyProperties[T, U any](dst T, src U) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("%v", e)
|
||||
}
|
||||
}()
|
||||
|
||||
dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
|
||||
srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)
|
||||
|
||||
if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
|
||||
return errors.New("CopyProperties: param dst should be struct pointer")
|
||||
}
|
||||
|
||||
if srcType.Kind() == reflect.Ptr {
|
||||
srcType, srcValue = srcType.Elem(), srcValue.Elem()
|
||||
}
|
||||
if srcType.Kind() != reflect.Struct {
|
||||
return errors.New("CopyProperties: param src should be a struct or struct pointer")
|
||||
}
|
||||
|
||||
dstType, dstValue = dstType.Elem(), dstValue.Elem()
|
||||
|
||||
propertyNums := dstType.NumField()
|
||||
|
||||
for i := 0; i < propertyNums; i++ {
|
||||
property := dstType.Field(i)
|
||||
propertyValue := srcValue.FieldByName(property.Name)
|
||||
|
||||
if !propertyValue.IsValid() || property.Type != propertyValue.Type() {
|
||||
continue
|
||||
}
|
||||
|
||||
if dstValue.Field(i).CanSet() {
|
||||
dstValue.Field(i).Set(propertyValue)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -252,3 +252,94 @@ func ExampleDecodeByte() {
|
||||
// Output:
|
||||
// abc
|
||||
}
|
||||
|
||||
func ExampleDeepClone() {
|
||||
type Struct struct {
|
||||
Str string
|
||||
Int int
|
||||
Float float64
|
||||
Bool bool
|
||||
Nil interface{}
|
||||
// unexported string
|
||||
}
|
||||
|
||||
cases := []interface{}{
|
||||
true,
|
||||
1,
|
||||
0.1,
|
||||
map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
},
|
||||
&Struct{
|
||||
Str: "test",
|
||||
Int: 1,
|
||||
Float: 0.1,
|
||||
Bool: true,
|
||||
Nil: nil,
|
||||
// unexported: "can't be cloned",
|
||||
},
|
||||
}
|
||||
|
||||
for _, item := range cases {
|
||||
cloned := DeepClone(item)
|
||||
|
||||
isPointerEqual := &cloned == &item
|
||||
fmt.Println(cloned, isPointerEqual)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true false
|
||||
// 1 false
|
||||
// 0.1 false
|
||||
// map[a:1 b:2] false
|
||||
// &{test 1 0.1 true <nil>} false
|
||||
}
|
||||
|
||||
func ExampleCopyProperties() {
|
||||
type Address struct {
|
||||
Country string
|
||||
ZipCode string
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
Role string
|
||||
Addr Address
|
||||
Hobbys []string
|
||||
salary int
|
||||
}
|
||||
|
||||
type Employee struct {
|
||||
Name string
|
||||
Age int
|
||||
Role string
|
||||
Addr Address
|
||||
Hobbys []string
|
||||
salary int
|
||||
}
|
||||
|
||||
user := User{Name: "user001", Age: 10, Role: "Admin", Addr: Address{Country: "CN", ZipCode: "001"}, Hobbys: []string{"a", "b"}, salary: 1000}
|
||||
|
||||
employee1 := Employee{}
|
||||
err := CopyProperties(&employee1, &user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
employee2 := Employee{Name: "employee001", Age: 20, Role: "User",
|
||||
Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500}
|
||||
|
||||
err = CopyProperties(&employee2, &user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(employee1)
|
||||
fmt.Println(employee2)
|
||||
|
||||
// Output:
|
||||
// {user001 10 Admin {CN 001} [a b] 0}
|
||||
// {user001 10 Admin {CN 001} [a b] 500}
|
||||
}
|
||||
|
||||
216
convertor/convertor_internal.go
Normal file
216
convertor/convertor_internal.go
Normal file
@@ -0,0 +1,216 @@
|
||||
// Copyright 2023 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
// Package convertor implements some functions to convert data.
|
||||
package convertor
|
||||
|
||||
import "reflect"
|
||||
|
||||
type cloner struct {
|
||||
ptrs map[reflect.Type]map[uintptr]reflect.Value
|
||||
}
|
||||
|
||||
// clone return a duplicate of passed item.
|
||||
func (c *cloner) clone(v reflect.Value) reflect.Value {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
return reflect.ValueOf(nil)
|
||||
|
||||
// bool
|
||||
case reflect.Bool:
|
||||
return reflect.ValueOf(v.Bool())
|
||||
|
||||
//int
|
||||
case reflect.Int:
|
||||
return reflect.ValueOf(int(v.Int()))
|
||||
case reflect.Int8:
|
||||
return reflect.ValueOf(int8(v.Int()))
|
||||
case reflect.Int16:
|
||||
return reflect.ValueOf(int16(v.Int()))
|
||||
case reflect.Int32:
|
||||
return reflect.ValueOf(int32(v.Int()))
|
||||
case reflect.Int64:
|
||||
return reflect.ValueOf(v.Int())
|
||||
|
||||
// uint
|
||||
case reflect.Uint:
|
||||
return reflect.ValueOf(uint(v.Uint()))
|
||||
case reflect.Uint8:
|
||||
return reflect.ValueOf(uint8(v.Uint()))
|
||||
case reflect.Uint16:
|
||||
return reflect.ValueOf(uint16(v.Uint()))
|
||||
case reflect.Uint32:
|
||||
return reflect.ValueOf(uint32(v.Uint()))
|
||||
case reflect.Uint64:
|
||||
return reflect.ValueOf(v.Uint())
|
||||
|
||||
// float
|
||||
case reflect.Float32:
|
||||
return reflect.ValueOf(float32(v.Float()))
|
||||
case reflect.Float64:
|
||||
return reflect.ValueOf(v.Float())
|
||||
|
||||
// complex
|
||||
case reflect.Complex64:
|
||||
return reflect.ValueOf(complex64(v.Complex()))
|
||||
case reflect.Complex128:
|
||||
return reflect.ValueOf(v.Complex())
|
||||
|
||||
// string
|
||||
case reflect.String:
|
||||
return reflect.ValueOf(v.String())
|
||||
|
||||
// array
|
||||
case reflect.Array, reflect.Slice:
|
||||
return c.cloneArray(v)
|
||||
|
||||
// map
|
||||
case reflect.Map:
|
||||
return c.cloneMap(v)
|
||||
|
||||
// Ptr
|
||||
case reflect.Ptr:
|
||||
return c.clonePtr(v)
|
||||
|
||||
// struct
|
||||
case reflect.Struct:
|
||||
return c.cloneStruct(v)
|
||||
|
||||
// func
|
||||
case reflect.Func:
|
||||
return v
|
||||
|
||||
// interface
|
||||
case reflect.Interface:
|
||||
return c.clone(v.Elem())
|
||||
|
||||
}
|
||||
|
||||
return reflect.Zero(v.Type())
|
||||
}
|
||||
|
||||
func (c *cloner) cloneArray(v reflect.Value) reflect.Value {
|
||||
if v.IsNil() {
|
||||
return reflect.Zero(v.Type())
|
||||
}
|
||||
|
||||
arr := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
val := c.clone(v.Index(i))
|
||||
|
||||
if val.IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
item := arr.Index(i)
|
||||
if !item.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
item.Set(val.Convert(item.Type()))
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
func (c *cloner) cloneMap(v reflect.Value) reflect.Value {
|
||||
if v.IsNil() {
|
||||
return reflect.Zero(v.Type())
|
||||
}
|
||||
|
||||
clonedMap := reflect.MakeMap(v.Type())
|
||||
|
||||
for _, key := range v.MapKeys() {
|
||||
value := v.MapIndex(key)
|
||||
clonedKey := c.clone(key)
|
||||
clonedValue := c.clone(value)
|
||||
|
||||
if !isNillable(clonedKey) || !clonedKey.IsNil() {
|
||||
clonedKey = clonedKey.Convert(key.Type())
|
||||
}
|
||||
|
||||
if (!isNillable(clonedValue) || !clonedValue.IsNil()) && clonedValue.IsValid() {
|
||||
clonedValue = clonedValue.Convert(value.Type())
|
||||
}
|
||||
|
||||
if !clonedValue.IsValid() {
|
||||
clonedValue = reflect.Zero(clonedMap.Type().Elem())
|
||||
}
|
||||
|
||||
clonedMap.SetMapIndex(clonedKey, clonedValue)
|
||||
}
|
||||
|
||||
return clonedMap
|
||||
}
|
||||
|
||||
func isNillable(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Interface, reflect.Ptr, reflect.Func:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *cloner) clonePtr(v reflect.Value) reflect.Value {
|
||||
if v.IsNil() {
|
||||
return reflect.Zero(v.Type())
|
||||
}
|
||||
|
||||
var newVal reflect.Value
|
||||
|
||||
if v.Elem().CanAddr() {
|
||||
ptrs, exists := c.ptrs[v.Type()]
|
||||
if exists {
|
||||
if newVal, exists := ptrs[v.Elem().UnsafeAddr()]; exists {
|
||||
return newVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newVal = c.clone(v.Elem())
|
||||
|
||||
if v.Elem().CanAddr() {
|
||||
ptrs, exists := c.ptrs[v.Type()]
|
||||
if exists {
|
||||
if newVal, exists := ptrs[v.Elem().UnsafeAddr()]; exists {
|
||||
return newVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clonedPtr := reflect.New(newVal.Type())
|
||||
clonedPtr.Elem().Set(newVal)
|
||||
|
||||
return clonedPtr
|
||||
}
|
||||
|
||||
func (c *cloner) cloneStruct(v reflect.Value) reflect.Value {
|
||||
clonedStructPtr := reflect.New(v.Type())
|
||||
clonedStruct := clonedStructPtr.Elem()
|
||||
|
||||
if v.CanAddr() {
|
||||
ptrs := c.ptrs[clonedStructPtr.Type()]
|
||||
if ptrs == nil {
|
||||
ptrs = make(map[uintptr]reflect.Value)
|
||||
c.ptrs[clonedStructPtr.Type()] = ptrs
|
||||
}
|
||||
ptrs[v.UnsafeAddr()] = clonedStructPtr
|
||||
}
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
newStructValue := clonedStruct.Field(i)
|
||||
if !newStructValue.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
clonedVal := c.clone(v.Field(i))
|
||||
if !clonedVal.IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
newStructValue.Set(clonedVal.Convert(newStructValue.Type()))
|
||||
}
|
||||
|
||||
return clonedStruct
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package convertor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@@ -179,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) {
|
||||
@@ -253,3 +272,97 @@ func TestDecodeByte(t *testing.T) {
|
||||
assert.IsNil(err)
|
||||
assert.Equal("abc", obj)
|
||||
}
|
||||
|
||||
func TestDeepClone(t *testing.T) {
|
||||
// assert := internal.NewAssert(t, "TestDeepClone")
|
||||
|
||||
type Struct struct {
|
||||
Str string
|
||||
Int int
|
||||
Float float64
|
||||
Bool bool
|
||||
Nil interface{}
|
||||
// unexported string
|
||||
}
|
||||
|
||||
cases := []interface{}{
|
||||
true,
|
||||
1,
|
||||
0.1,
|
||||
map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
},
|
||||
&Struct{
|
||||
Str: "test",
|
||||
Int: 1,
|
||||
Float: 0.1,
|
||||
Bool: true,
|
||||
Nil: nil,
|
||||
// unexported: "can't be cloned",
|
||||
},
|
||||
}
|
||||
|
||||
for i, item := range cases {
|
||||
cloned := DeepClone(item)
|
||||
|
||||
t.Log(cloned)
|
||||
if &cloned == &item {
|
||||
t.Fatalf("[TestDeepClone case #%d failed]: equal pointer", i)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(item, cloned) {
|
||||
t.Fatalf("[TestDeepClone case #%d failed] unequal objects", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyProperties(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCopyProperties")
|
||||
|
||||
type Address struct {
|
||||
Country string
|
||||
ZipCode string
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
Role string
|
||||
Addr Address
|
||||
Hobbys []string
|
||||
salary int
|
||||
}
|
||||
|
||||
type Employee struct {
|
||||
Name string
|
||||
Age int
|
||||
Role string
|
||||
Addr Address
|
||||
Hobbys []string
|
||||
salary int
|
||||
}
|
||||
|
||||
user := User{Name: "user001", Age: 10, Role: "Admin", Addr: Address{Country: "CN", ZipCode: "001"}, Hobbys: []string{"a", "b"}, salary: 1000}
|
||||
|
||||
employee1 := Employee{}
|
||||
|
||||
err := CopyProperties(&employee1, &user)
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal("user001", employee1.Name)
|
||||
assert.Equal("Admin", employee1.Role)
|
||||
assert.Equal("CN", employee1.Addr.Country)
|
||||
assert.Equal(0, employee1.salary)
|
||||
|
||||
employee2 := Employee{Name: "employee001", Age: 20, Role: "User",
|
||||
Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500}
|
||||
|
||||
err = CopyProperties(&employee2, &user)
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal("user001", employee2.Name)
|
||||
assert.Equal("Admin", employee2.Role)
|
||||
assert.Equal("CN", employee2.Addr.Country)
|
||||
assert.Equal(500, employee2.salary)
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package cryptor
|
||||
|
||||
import "bytes"
|
||||
|
||||
func generateAesKey(key []byte) []byte {
|
||||
genKey := make([]byte, 16)
|
||||
func generateAesKey(key []byte, size int) []byte {
|
||||
genKey := make([]byte, size)
|
||||
copy(genKey, key)
|
||||
for i := 16; i < len(key); {
|
||||
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
|
||||
for i := size; i < len(key); {
|
||||
for j := 0; j < size && i < len(key); j, i = j+1, i+1 {
|
||||
genKey[j] ^= key[i]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,11 @@ import (
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/jT5irszHx-j
|
||||
func AesEcbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
length := (len(data) + aes.BlockSize) / aes.BlockSize
|
||||
plain := make([]byte, length*aes.BlockSize)
|
||||
|
||||
@@ -34,7 +39,7 @@ func AesEcbEncrypt(data, key []byte) []byte {
|
||||
}
|
||||
|
||||
encrypted := make([]byte, len(plain))
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key))
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key, size))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs <= len(data); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
||||
@@ -47,7 +52,11 @@ func AesEcbEncrypt(data, key []byte) []byte {
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/jT5irszHx-j
|
||||
func AesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key))
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
cipher, _ := aes.NewCipher(generateAesKey(key, size))
|
||||
decrypted := make([]byte, len(encrypted))
|
||||
|
||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
|
||||
@@ -17,7 +17,7 @@ type mapNode struct {
|
||||
next *mapNode
|
||||
}
|
||||
|
||||
//HashMap implements a hash map
|
||||
// HashMap implements a hash map
|
||||
type HashMap struct {
|
||||
capacity uint64
|
||||
size uint64
|
||||
|
||||
@@ -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,15 +1,17 @@
|
||||
# Convertor
|
||||
|
||||
Package convertor contains some functions for data type convertion.
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Source:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/convertor/convertor.go](https://github.com/duke-git/lancet/blob/main/convertor/convertor.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/convertor/convertor.go](https://github.com/duke-git/lancet/blob/main/convertor/convertor.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Usage:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
@@ -19,28 +21,32 @@ import (
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Index
|
||||
- [ColorHexToRGB](#ColorHexToRGB)
|
||||
- [ColorRGBToHex](#ColorRGBToHex)
|
||||
- [ToBool](#ToBool)
|
||||
- [ToBytes](#ToBytes)
|
||||
- [ToChar](#ToChar)
|
||||
- [ToChannel](#ToChannel)
|
||||
- [ToFloat](#ToFloat)
|
||||
- [ToInt](#ToInt)
|
||||
- [ToJson](#ToJson)
|
||||
- [ToMap](#ToMap)
|
||||
- [ToPointer](#ToPointer)
|
||||
- [ToString](#ToString)
|
||||
- [StructToMap](#StructToMap)
|
||||
- [MapToSlice](#MapToSlice)
|
||||
- [EncodeByte](#EncodeByte)
|
||||
- [DecodeByte](#DecodeByte)
|
||||
|
||||
- [ColorHexToRGB](#ColorHexToRGB)
|
||||
- [ColorRGBToHex](#ColorRGBToHex)
|
||||
- [ToBool](#ToBool)
|
||||
- [ToBytes](#ToBytes)
|
||||
- [ToChar](#ToChar)
|
||||
- [ToChannel](#ToChannel)
|
||||
- [ToFloat](#ToFloat)
|
||||
- [ToInt](#ToInt)
|
||||
- [ToJson](#ToJson)
|
||||
- [ToMap](#ToMap)
|
||||
- [ToPointer](#ToPointer)
|
||||
- [ToString](#ToString)
|
||||
- [StructToMap](#StructToMap)
|
||||
- [MapToSlice](#MapToSlice)
|
||||
- [EncodeByte](#EncodeByte)
|
||||
- [DecodeByte](#DecodeByte)
|
||||
- [DeepClone](#DeepClone)
|
||||
- [CopyProperties](#CopyProperties)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="ColorHexToRGB">ColorHexToRGB</span>
|
||||
|
||||
<p>Convert color hex to color rgb.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -48,6 +54,7 @@ import (
|
||||
```go
|
||||
func ColorHexToRGB(colorHex string) (red, green, blue int)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -78,6 +85,7 @@ func main() {
|
||||
```go
|
||||
func ColorRGBToHex(red, green, blue int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -110,6 +118,7 @@ func main() {
|
||||
```go
|
||||
func ToBool(s string) (bool, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -150,6 +159,7 @@ func main() {
|
||||
```go
|
||||
func ToBytes(data any) ([]byte, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -182,6 +192,7 @@ func main() {
|
||||
```go
|
||||
func ToChar(s string) []string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -217,6 +228,7 @@ func main() {
|
||||
```go
|
||||
func ToChannel[T any](array []T) <-chan T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -253,6 +265,7 @@ func main() {
|
||||
```go
|
||||
func ToFloat(value any) (float64, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -297,6 +310,7 @@ func main() {
|
||||
```go
|
||||
func ToInt(value any) (int64, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -338,6 +352,7 @@ func main() {
|
||||
```go
|
||||
func ToJson(value any) (string, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -372,6 +387,7 @@ func main() {
|
||||
```go
|
||||
func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -412,6 +428,7 @@ func main() {
|
||||
```go
|
||||
func ToPointer[T any](value T) *T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -424,8 +441,8 @@ import (
|
||||
|
||||
func main() {
|
||||
result := convertor.ToPointer(123)
|
||||
fmt.Println(*result)
|
||||
|
||||
fmt.Println(*result)
|
||||
|
||||
// Output:
|
||||
// 123
|
||||
}
|
||||
@@ -440,6 +457,7 @@ func main() {
|
||||
```go
|
||||
func ToString(value any) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -487,6 +505,7 @@ func main() {
|
||||
```go
|
||||
func StructToMap(value any) (map[string]any, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -524,6 +543,7 @@ func main() {
|
||||
```go
|
||||
func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -544,7 +564,6 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="EncodeByte">EncodeByte</span>
|
||||
|
||||
<p>Encode data to byte slice.</p>
|
||||
@@ -554,6 +573,7 @@ func main() {
|
||||
```go
|
||||
func EncodeByte(data any) ([]byte, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -582,6 +602,7 @@ func main() {
|
||||
```go
|
||||
func DecodeByte(data []byte, target any) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
@@ -595,15 +616,144 @@ import (
|
||||
func main() {
|
||||
var result string
|
||||
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
|
||||
|
||||
|
||||
err := convertor.DecodeByte(byteData, &result)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
|
||||
// Output:
|
||||
// abc
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="DeepClone">DeepClone</span>
|
||||
|
||||
<p>Creates a deep copy of passed item, can't clone unexported field of struct.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func DeepClone[T any](src T) T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Struct struct {
|
||||
Str string
|
||||
Int int
|
||||
Float float64
|
||||
Bool bool
|
||||
Nil interface{}
|
||||
unexported string
|
||||
}
|
||||
|
||||
cases := []interface{}{
|
||||
true,
|
||||
1,
|
||||
0.1,
|
||||
map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
},
|
||||
&Struct{
|
||||
Str: "test",
|
||||
Int: 1,
|
||||
Float: 0.1,
|
||||
Bool: true,
|
||||
Nil: nil,
|
||||
// unexported: "can't be cloned",
|
||||
},
|
||||
}
|
||||
|
||||
for _, item := range cases {
|
||||
cloned := convertor.DeepClone(item)
|
||||
|
||||
isPointerEqual := &cloned == &item
|
||||
fmt.Println(cloned, isPointerEqual)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true false
|
||||
// 1 false
|
||||
// 0.1 false
|
||||
// map[a:1 b:2] false
|
||||
// &{test 1 0.1 true <nil> } false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="CopyProperties">CopyProperties</span>
|
||||
|
||||
<p>Copies each field from the source struct into the destination struct.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CopyProperties[T, U any](dst T, src U) (err error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Address struct {
|
||||
Country string
|
||||
ZipCode string
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
Role string
|
||||
Addr Address
|
||||
Hobbys []string
|
||||
salary int
|
||||
}
|
||||
|
||||
type Employee struct {
|
||||
Name string
|
||||
Age int
|
||||
Role string
|
||||
Addr Address
|
||||
Hobbys []string
|
||||
salary int
|
||||
}
|
||||
|
||||
user := User{Name: "user001", Age: 10, Role: "Admin", Addr: Address{Country: "CN", ZipCode: "001"}, Hobbys: []string{"a", "b"}, salary: 1000}
|
||||
|
||||
employee1 := Employee{}
|
||||
CopyProperties(&employee1, &user)
|
||||
|
||||
employee2 := Employee{Name: "employee001", Age: 20, Role: "User",
|
||||
Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500}
|
||||
|
||||
CopyProperties(&employee2, &user)
|
||||
|
||||
fmt.Println(employee1)
|
||||
fmt.Println(employee2)
|
||||
|
||||
// Output:
|
||||
// {user001 10 Admin {CN 001} [a b] 0}
|
||||
// {user001 10 Admin {CN 001} [a b] 500}
|
||||
}
|
||||
```
|
||||
@@ -1,11 +1,12 @@
|
||||
# Convertor
|
||||
convertor转换器包支持一些常见的数据类型转换
|
||||
|
||||
convertor 转换器包支持一些常见的数据类型转换
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 源码:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/convertor/convertor.go](https://github.com/duke-git/lancet/blob/main/convertor/convertor.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/convertor/convertor.go](https://github.com/duke-git/lancet/blob/main/convertor/convertor.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -21,29 +22,32 @@ import (
|
||||
|
||||
## 目录
|
||||
|
||||
- [ColorHexToRGB](#ColorHexToRGB)
|
||||
- [ColorRGBToHex](#ColorRGBToHex)
|
||||
- [ToBool](#ToBool)
|
||||
- [ToBytes](#ToBytes)
|
||||
- [ToChar](#ToChar)
|
||||
- [ToChannel](#ToChannel)
|
||||
- [ToFloat](#ToFloat)
|
||||
- [ToInt](#ToInt)
|
||||
- [ToJson](#ToJson)
|
||||
- [ToMap](#ToMap)
|
||||
- [ToPointer](#ToPointer)
|
||||
- [ToString](#ToString)
|
||||
- [StructToMap](#StructToMap)
|
||||
- [MapToSlice](#MapToSlice)
|
||||
- [EncodeByte](#EncodeByte)
|
||||
- [DecodeByte](#DecodeByte)
|
||||
- [ColorHexToRGB](#ColorHexToRGB)
|
||||
- [ColorRGBToHex](#ColorRGBToHex)
|
||||
- [ToBool](#ToBool)
|
||||
- [ToBytes](#ToBytes)
|
||||
- [ToChar](#ToChar)
|
||||
- [ToChannel](#ToChannel)
|
||||
- [ToFloat](#ToFloat)
|
||||
- [ToInt](#ToInt)
|
||||
- [ToJson](#ToJson)
|
||||
- [ToMap](#ToMap)
|
||||
- [ToPointer](#ToPointer)
|
||||
- [ToString](#ToString)
|
||||
- [StructToMap](#StructToMap)
|
||||
- [MapToSlice](#MapToSlice)
|
||||
- [EncodeByte](#EncodeByte)
|
||||
- [DecodeByte](#DecodeByte)
|
||||
- [DeepClone](#DeepClone)
|
||||
- [CopyProperties](#CopyProperties)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
|
||||
### <span id="ColorHexToRGB">ColorHexToRGB</span>
|
||||
|
||||
<p>颜色值十六进制转rgb。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
@@ -51,6 +55,7 @@ import (
|
||||
```go
|
||||
func ColorHexToRGB(colorHex string) (red, green, blue int)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -81,6 +86,7 @@ func main() {
|
||||
```go
|
||||
func ColorRGBToHex(red, green, blue int) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -113,6 +119,7 @@ func main() {
|
||||
```go
|
||||
func ToBool(s string) (bool, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -153,6 +160,7 @@ func main() {
|
||||
```go
|
||||
func ToBytes(data any) ([]byte, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -185,6 +193,7 @@ func main() {
|
||||
```go
|
||||
func ToChar(s string) []string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -220,6 +229,7 @@ func main() {
|
||||
```go
|
||||
func ToChannel[T any](array []T) <-chan T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -256,6 +266,7 @@ func main() {
|
||||
```go
|
||||
func ToFloat(value any) (float64, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -300,6 +311,7 @@ func main() {
|
||||
```go
|
||||
func ToInt(value any) (int64, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -341,6 +353,7 @@ func main() {
|
||||
```go
|
||||
func ToJson(value any) (string, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -375,6 +388,7 @@ func main() {
|
||||
```go
|
||||
func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -415,6 +429,7 @@ func main() {
|
||||
```go
|
||||
func ToPointer[T any](value T) *T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -427,8 +442,8 @@ import (
|
||||
|
||||
func main() {
|
||||
result := convertor.ToPointer(123)
|
||||
fmt.Println(*result)
|
||||
|
||||
fmt.Println(*result)
|
||||
|
||||
// Output:
|
||||
// 123
|
||||
}
|
||||
@@ -443,6 +458,7 @@ func main() {
|
||||
```go
|
||||
func ToString(value any) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -490,6 +506,7 @@ func main() {
|
||||
```go
|
||||
func StructToMap(value any) (map[string]any, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -527,6 +544,7 @@ func main() {
|
||||
```go
|
||||
func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -556,6 +574,7 @@ func main() {
|
||||
```go
|
||||
func EncodeByte(data any) ([]byte, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -584,6 +603,7 @@ func main() {
|
||||
```go
|
||||
func DecodeByte(data []byte, target any) error
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
@@ -597,15 +617,142 @@ import (
|
||||
func main() {
|
||||
var result string
|
||||
byteData := []byte{6, 12, 0, 3, 97, 98, 99}
|
||||
|
||||
|
||||
err := convertor.DecodeByte(byteData, &result)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
|
||||
// Output:
|
||||
// abc
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DeepClone">DeepClone</span>
|
||||
|
||||
<p>创建一个传入值的深拷贝, 无法克隆结构体的非导出字段。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DeepClone[T any](src T) T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Struct struct {
|
||||
Str string
|
||||
Int int
|
||||
Float float64
|
||||
Bool bool
|
||||
Nil interface{}
|
||||
unexported string
|
||||
}
|
||||
|
||||
cases := []interface{}{
|
||||
true,
|
||||
1,
|
||||
0.1,
|
||||
map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
},
|
||||
&Struct{
|
||||
Str: "test",
|
||||
Int: 1,
|
||||
Float: 0.1,
|
||||
Bool: true,
|
||||
Nil: nil,
|
||||
// unexported: "can't be cloned",
|
||||
},
|
||||
}
|
||||
|
||||
for _, item := range cases {
|
||||
cloned := convertor.DeepClone(item)
|
||||
|
||||
isPointerEqual := &cloned == &item
|
||||
fmt.Println(cloned, isPointerEqual)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// true false
|
||||
// 1 false
|
||||
// 0.1 false
|
||||
// map[a:1 b:2] false
|
||||
// &{test 1 0.1 true <nil> } false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CopyProperties">CopyProperties</span>
|
||||
|
||||
<p>拷贝不同结构体之间的同名字段。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CopyProperties[T, U any](dst T, src U) (err error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type Address struct {
|
||||
Country string
|
||||
ZipCode string
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
Role string
|
||||
Addr Address
|
||||
Hobbys []string
|
||||
salary int
|
||||
}
|
||||
|
||||
type Employee struct {
|
||||
Name string
|
||||
Age int
|
||||
Role string
|
||||
Addr Address
|
||||
Hobbys []string
|
||||
salary int
|
||||
}
|
||||
|
||||
user := User{Name: "user001", Age: 10, Role: "Admin", Addr: Address{Country: "CN", ZipCode: "001"}, Hobbys: []string{"a", "b"}, salary: 1000}
|
||||
|
||||
employee1 := Employee{}
|
||||
CopyProperties(&employee1, &user)
|
||||
|
||||
employee2 := Employee{Name: "employee001", Age: 20, Role: "User",
|
||||
Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500}
|
||||
|
||||
CopyProperties(&employee2, &user)
|
||||
|
||||
fmt.Println(employee1)
|
||||
fmt.Println(employee2)
|
||||
|
||||
// Output:
|
||||
// {user001 10 Admin {CN 001} [a b] 0}
|
||||
// {user001 10 Admin {CN 001} [a b] 500}
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
- [Compose](#Compose)
|
||||
- [Debounced](#Debounced)
|
||||
- [Delay](#Delay)
|
||||
- [Schedule](#Schedule)
|
||||
- [Pipeline](#Pipeline)
|
||||
- [Watcher](#Watcher)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
- [Compose](#Compose)
|
||||
- [Debounced](#Debounced)
|
||||
- [Delay](#Delay)
|
||||
- [Schedule](#Schedule)
|
||||
- [Pipeline](#Pipeline)
|
||||
- [Watcher](#Watcher)
|
||||
|
||||
@@ -206,7 +207,7 @@ import (
|
||||
|
||||
func main() {
|
||||
count := 0
|
||||
|
||||
|
||||
add := func() {
|
||||
count++
|
||||
}
|
||||
|
||||
509
docs/maputil.md
509
docs/maputil.md
@@ -24,11 +24,23 @@ import (
|
||||
|
||||
- [ForEach](#ForEach)
|
||||
- [Filter](#Filter)
|
||||
- [FilterByKeys](#FilterByKeys)
|
||||
- [FilterByValues](#FilterByValues)
|
||||
- [OmitBy](#OmitBy)
|
||||
- [OmitByKeys](#OmitByKeys)
|
||||
- [OmitByValues](#OmitByValues)
|
||||
- [Intersect](#Intersect)
|
||||
- [Keys](#Keys)
|
||||
- [Values](#Values)
|
||||
- [KeysBy](#KeysBy)
|
||||
- [ValuesBy](#ValuesBy)
|
||||
- [MapKeys](#MapKeys)
|
||||
- [MapValues](#MapValues)
|
||||
- [Entries](#Entries)
|
||||
- [FromEntries](#FromEntries)
|
||||
- [Transform](#Transform)
|
||||
- [Merge](#Merge)
|
||||
- [Minus](#Minus)
|
||||
- [Values](#Values)
|
||||
- [IsDisjoint](#IsDisjoint)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
@@ -121,6 +133,205 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="FilterByKeys">FilterByKeys</span>
|
||||
|
||||
<p>Iterates over map, return a new map whose keys are all given keys.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func FilterByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := maputil.FilterByKeys(m, []string{"a", "b"})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="FilterByValues">FilterByValues</span>
|
||||
|
||||
<p>Iterates over map, return a new map whose values are all given values.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func FilterByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := maputil.FilterByValues(m, []int{3, 4})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[c:3 d:4]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="OmitBy">OmitBy</span>
|
||||
|
||||
<p>OmitBy is the opposite of Filter, removes all the map elements for which the predicate function returns true.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func OmitBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) map[K]V
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
isEven := func(_ string, value int) bool {
|
||||
return value%2 == 0
|
||||
}
|
||||
|
||||
result := maputil.OmitBy(m, isEven)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 c:3 e:5]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="OmitByKeys">OmitByKeys</span>
|
||||
|
||||
<p>The opposite of FilterByKeys, extracts all the map elements which keys are not omitted.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func OmitByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := maputil.OmitByKeys(m, []string{"a", "b"})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[c:3 d:4 e:5]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="OmitByValues">OmitByValues</span>
|
||||
|
||||
<p>The opposite of FilterByValues. remov all elements whose value are in the give slice.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func OmitByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := maputil.OmitByValues(m, []int{4, 5})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Intersect">Intersect</span>
|
||||
|
||||
<p>Iterates over maps, return a new map of key and value pairs in all given maps.</p>
|
||||
@@ -193,6 +404,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
@@ -313,6 +525,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
@@ -335,6 +548,300 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="KeysBy">KeysBy</span>
|
||||
|
||||
<p>Creates a slice whose element is the result of function mapper invoked by every map's key.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func KeysBy[K comparable, V any, T any](m map[K]V, mapper func(item K) T) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "a",
|
||||
3: "b",
|
||||
}
|
||||
|
||||
keys := maputil.KeysBy(m, func(n int) int {
|
||||
return n + 1
|
||||
})
|
||||
|
||||
sort.Ints(keys)
|
||||
|
||||
fmt.Println(keys)
|
||||
|
||||
// Output:
|
||||
// [2 3 4]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ValuesBy">ValuesBy</span>
|
||||
|
||||
<p>Creates a slice whose element is the result of function mapper invoked by every map's value.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
values := maputil.ValuesBy(m, func(v string) string {
|
||||
switch v {
|
||||
case "a":
|
||||
return "a-1"
|
||||
case "b":
|
||||
return "b-2"
|
||||
case "c":
|
||||
return "c-3"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
})
|
||||
|
||||
sort.Strings(values)
|
||||
|
||||
fmt.Println(values)
|
||||
|
||||
// Output:
|
||||
// [a-1 b-2 c-3]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="MapKeys">MapKeys</span>
|
||||
|
||||
<p>Transforms a map to other type map by manipulating it's keys.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func MapKeys[K comparable, V any, T comparable](m map[K]V, iteratee func(key K, value V) T) map[T]V
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
|
||||
result := maputil.MapKeys(m, func(k int, _ string) string {
|
||||
return strconv.Itoa(k)
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[1:a 2:b 3:c]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="MapValues">MapValues</span>
|
||||
|
||||
<p>Transforms a map to other type map by manipulating it's values.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func MapValues[K comparable, V any, T any](m map[K]V, iteratee func(key K, value V) T) map[K]T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
|
||||
result := maputil.MapValues(m, func(k int, v string) string {
|
||||
return v + strconv.Itoa(k)
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[1:a1 2:b2 3:c3]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Entry">Entry</span>
|
||||
|
||||
<p>Transforms a map into array of key/value pairs.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type Entry[K comparable, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
func Entries[K comparable, V any](m map[K]V) []Entry[K, V]
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
|
||||
result := maputil.Entries(m)
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].Value < result[j].Value
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// [{a 1} {b 2} {c 3}]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="FromEntries">FromEntries</span>
|
||||
|
||||
<p>Creates a map based on a slice of key/value pairs.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type Entry[K comparable, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := maputil.FromEntries([]Entry[string, int]{
|
||||
{Key: "a", Value: 1},
|
||||
{Key: "b", Value: 2},
|
||||
{Key: "c", Value: 3},
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Transform">Transform</span>
|
||||
|
||||
<p>Transform a map to another type map.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Transform[K1 comparable, V1 any, K2 comparable, V2 any](m map[K1]V1, iteratee func(key K1, value V1) (K2, V2)) map[K2]V2
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
|
||||
result := Transform(m, func(k string, v int) (string, string) {
|
||||
return k, strconv.Itoa(v)
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="IsDisjoint">IsDisjoint</span>
|
||||
|
||||
<p>Checks two maps are disjoint if they have no keys in common</p>
|
||||
|
||||
@@ -24,11 +24,23 @@ import (
|
||||
|
||||
- [ForEach](#ForEach)
|
||||
- [Filter](#Filter)
|
||||
- [FilterByKeys](#FilterByKeys)
|
||||
- [FilterByValues](#FilterByValues)
|
||||
- [OmitBy](#OmitBy)
|
||||
- [OmitByKeys](#OmitByKeys)
|
||||
- [OmitByValues](#OmitByValues)
|
||||
- [Intersect](#Intersect)
|
||||
- [Keys](#Keys)
|
||||
- [Values](#Values)
|
||||
- [KeysBy](#KeysBy)
|
||||
- [ValuesBy](#ValuesBy)
|
||||
- [MapKeys](#MapKeys)
|
||||
- [MapValues](#MapValues)
|
||||
- [Entries](#Entries)
|
||||
- [FromEntries](#FromEntries)
|
||||
- [Transform](#Transform)
|
||||
- [Merge](#Merge)
|
||||
- [Minus](#Minus)
|
||||
- [Values](#Values)
|
||||
- [IsDisjoint](#IsDisjoint)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
@@ -78,7 +90,7 @@ func main() {
|
||||
|
||||
### <span id="Filter">Filter</span>
|
||||
|
||||
<p>迭代map中的每对key和value, 返回符合predicate函数的key, value</p>
|
||||
<p>迭代map中的每对key和value, 返回符合predicate函数的key, value。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -121,6 +133,203 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FilterByKeys">FilterByKeys</span>
|
||||
|
||||
<p>迭代map, 返回一个新map,其key都是给定的key值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func FilterByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := maputil.FilterByKeys(m, []string{"a", "b"})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="FilterByValues">FilterByValues</span>
|
||||
|
||||
<p>迭代map, 返回一个新map,其value都是给定的value值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func FilterByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := maputil.FilterByValues(m, []int{3, 4})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[c:3 d:4]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="OmitBy">OmitBy</span>
|
||||
|
||||
<p>Filter的反向操作, 迭代map中的每对key和value, 删除符合predicate函数的key, value, 返回新map。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func OmitBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) map[K]V
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
isEven := func(_ string, value int) bool {
|
||||
return value%2 == 0
|
||||
}
|
||||
|
||||
result := maputil.OmitBy(m, isEven)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 c:3 e:5]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="OmitByKeys">OmitByKeys</span>
|
||||
|
||||
<p>FilterByKeys的反向操作, 迭代map, 返回一个新map,其key不包括给定的key值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func OmitByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := maputil.OmitByKeys(m, []string{"a", "b"})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[c:3 d:4 e:5]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="OmitByValues">OmitByValues</span>
|
||||
|
||||
<p>FilterByValues的反向操作, 迭代map, 返回一个新map,其value不包括给定的value值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func OmitByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := maputil.OmitByValues(m, []int{4, 5})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Intersect">Intersect</span>
|
||||
|
||||
<p>多个map的交集操作</p>
|
||||
@@ -333,6 +542,299 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="KeysBy">KeysBy</span>
|
||||
|
||||
<p>创建一个切片,其元素是每个map的key调用mapper函数的结果。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func KeysBy[K comparable, V any, T any](m map[K]V, mapper func(item K) T) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "a",
|
||||
3: "b",
|
||||
}
|
||||
|
||||
keys := maputil.KeysBy(m, func(n int) int {
|
||||
return n + 1
|
||||
})
|
||||
|
||||
sort.Ints(keys)
|
||||
|
||||
fmt.Println(keys)
|
||||
|
||||
// Output:
|
||||
// [2 3 4]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ValuesBy">ValuesBy</span>
|
||||
|
||||
<p>创建一个切片,其元素是每个map的value调用mapper函数的结果。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
values := maputil.ValuesBy(m, func(v string) string {
|
||||
switch v {
|
||||
case "a":
|
||||
return "a-1"
|
||||
case "b":
|
||||
return "b-2"
|
||||
case "c":
|
||||
return "c-3"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
})
|
||||
|
||||
sort.Strings(values)
|
||||
|
||||
fmt.Println(values)
|
||||
|
||||
// Output:
|
||||
// [a-1 b-2 c-3]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="MapKeys">MapKeys</span>
|
||||
|
||||
<p>操作map的每个key,然后转为新的map。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func MapKeys[K comparable, V any, T comparable](m map[K]V, iteratee func(key K, value V) T) map[T]V
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
|
||||
result := maputil.MapKeys(m, func(k int, _ string) string {
|
||||
return strconv.Itoa(k)
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[1:a 2:b 3:c]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="MapValues">MapValues</span>
|
||||
|
||||
<p>操作map的每个value,然后转为新的map。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func MapValues[K comparable, V any, T any](m map[K]V, iteratee func(key K, value V) T) map[K]T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
|
||||
result := maputil.MapValues(m, func(k int, v string) string {
|
||||
return v + strconv.Itoa(k)
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[1:a1 2:b2 3:c3]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="Entry">Entry</span>
|
||||
|
||||
<p>将map转换为键/值对切片。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type Entry[K comparable, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
func Entries[K comparable, V any](m map[K]V) []Entry[K, V]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
|
||||
result := maputil.Entries(m)
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].Value < result[j].Value
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// [{a 1} {b 2} {c 3}]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="FromEntries">FromEntries</span>
|
||||
|
||||
<p>基于键/值对的切片创建map。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type Entry[K comparable, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result := maputil.FromEntries([]Entry[string, int]{
|
||||
{Key: "a", Value: 1},
|
||||
{Key: "b", Value: 2},
|
||||
{Key: "c", Value: 3},
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Transform">Transform</span>
|
||||
|
||||
<p>将map转换为其他类型的map。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Transform[K1 comparable, V1 any, K2 comparable, V2 any](m map[K1]V1, iteratee func(key K1, value V1) (K2, V2)) map[K2]V2
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"github.com/duke-git/lancet/v2/maputil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
|
||||
result := Transform(m, func(k string, v int) (string, string) {
|
||||
return k, strconv.Itoa(v)
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsDisjoint">IsDisjoint</span>
|
||||
|
||||
<p>验证两个map是否具有不同的key</p>
|
||||
|
||||
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
|
||||
//
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
433
docs/slice.md
433
docs/slice.md
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
- [AppendIfAbsent](#AppendIfAbsent)
|
||||
- [Contain](#Contain)
|
||||
- [ContainBy](#ContainBy)
|
||||
- [ContainSubSlice](#ContainSubSlice)
|
||||
- [Chunk](#Chunk)
|
||||
- [Compact](#Compact)
|
||||
@@ -35,6 +36,9 @@ import (
|
||||
- [DifferenceWith](#DifferenceWith)
|
||||
- [DeleteAt](#DeleteAt)
|
||||
- [Drop](#Drop)
|
||||
- [DropRight](#DropRight)
|
||||
- [DropWhile](#DropWhile)
|
||||
- [DropRightWhile](#DropRightWhile)
|
||||
- [Equal](#Equal)
|
||||
- [EqualWith](#EqualWith)
|
||||
- [Every](#Every)
|
||||
@@ -44,6 +48,7 @@ import (
|
||||
- [Flatten](#Flatten)
|
||||
- [FlattenDeep](#FlattenDeep)
|
||||
- [ForEach](#ForEach)
|
||||
- [ForEachWithBreak](#ForEachWithBreak)
|
||||
- [GroupBy](#GroupBy)
|
||||
- [GroupWith](#GroupWith)
|
||||
- [IntSlice<sup>deprecated</sup>](#IntSlice)
|
||||
@@ -53,6 +58,8 @@ import (
|
||||
- [IndexOf](#IndexOf)
|
||||
- [LastIndexOf](#LastIndexOf)
|
||||
- [Map](#Map)
|
||||
- [FilterMap](#FilterMap)
|
||||
- [FlatMap](#FlatMap)
|
||||
- [Merge](#Merge)
|
||||
- [Reverse](#Reverse)
|
||||
- [Reduce](#Reduce)
|
||||
@@ -60,6 +67,10 @@ import (
|
||||
- [ReplaceAll](#ReplaceAll)
|
||||
- [Repeat](#Repeat)
|
||||
- [Shuffle](#Shuffle)
|
||||
- [IsAscending](#IsAscending)
|
||||
- [IsDescending](#IsDescending)
|
||||
- [IsSorted](#IsSorted)
|
||||
- [IsSortedByKey](#IsSortedByKey)
|
||||
- [Sort](#Sort)
|
||||
- [SortBy](#SortBy)
|
||||
- [SortByField<sup>deprecated</sup>](#SortByField)
|
||||
@@ -142,6 +153,51 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ContainBy">ContainBy</span>
|
||||
|
||||
<p>returns true if predicate function return true.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ContainBy[T any](slice []T, predicate func(item T) bool) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type foo struct {
|
||||
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 })
|
||||
|
||||
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)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ContainSubSlice">ContainSubSlice</span>
|
||||
|
||||
<p>Check if the slice contain subslice or not.</p>
|
||||
@@ -487,7 +543,7 @@ func main() {
|
||||
|
||||
### <span id="Drop">Drop</span>
|
||||
|
||||
<p>Creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0.</p>
|
||||
<p>Drop n elements from the start of a slice.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
@@ -517,11 +573,130 @@ func main() {
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [b c]
|
||||
// [a b]
|
||||
// [a b c]
|
||||
// []
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DropRight">DropRight</span>
|
||||
|
||||
<p>Drop n elements from the end of a slice.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func DropRight[T any](slice []T, n int) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [a b]
|
||||
// [a b c]
|
||||
// []
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DropWhile">DropWhile</span>
|
||||
|
||||
<p>Drop n elements from the start of a slice while predicate function returns true.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func DropWhile[T any](slice []T, predicate func(item T) bool) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [2 3 4 5]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DropRightWhile">DropRightWhile</span>
|
||||
|
||||
<p>Drop n elements from the end of a slice while predicate function returns true.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func DropRightWhile[T any](slice []T, predicate func(item T) bool) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Equal">Equal</span>
|
||||
|
||||
<p>Check if two slices are equal: the same length and all elements' order and value are equal.</p>
|
||||
@@ -827,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>
|
||||
@@ -1126,6 +1339,76 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FilterMap">FilterMap</span>
|
||||
|
||||
<p>Returns a slice which apply both filtering and mapping to the given slice. iteratee callback function should returntwo values: 1, mapping result. 2, whether the result element should be included or not.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func FilterMap[T any, U any](slice []T, iteratee func(index int, item T) (U, bool)) []U
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
result := slice.FilterMap(nums, getEvenNumStr)
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
|
||||
// Output:
|
||||
// []string{"2", "4"}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FlatMap">FlatMap</span>
|
||||
|
||||
<p>Manipulates a slice and transforms and flattens it to a slice of another type.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func FlatMap[T any, U any](slice []T, iteratee func(index int, item T) []U) []U
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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}
|
||||
})
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
|
||||
// Output:
|
||||
// []string{"hi-1", "hi-2", "hi-3", "hi-4"}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Merge">Merge</span>
|
||||
|
||||
<p>Merge all given slices into one slice.</p>
|
||||
@@ -1341,13 +1624,155 @@ 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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsAscending">IsAscending</span>
|
||||
|
||||
<p>Checks if a slice is ascending order.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsAscending[T constraints.Ordered](slice []T) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsDescending">IsDescending</span>
|
||||
|
||||
<p>Checks if a slice is descending order.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsDescending[T constraints.Ordered](slice []T) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsSorted">IsSorted</span>
|
||||
|
||||
<p>Checks if a slice is sorted (ascending or descending).</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsSorted[T constraints.Ordered](slice []T) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsSortedByKey">IsSortedByKey</span>
|
||||
|
||||
<p>Checks if a slice is sorted by iteratee function.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func IsSortedByKey[T any, K constraints.Ordered](slice []T, iteratee func(item T) K) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Sort">Sort</span>
|
||||
|
||||
<p>Sorts a slice of any ordered type(number or string), use quick sort algrithm. Default sort order is ascending (asc), if want descending order, set param `sortOrder` to `desc`. Ordered type: number(all ints uints floats) or string.
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
- [AppendIfAbsent](#AppendIfAbsent)
|
||||
- [Contain](#Contain)
|
||||
- [ContainBy](#ContainBy)
|
||||
- [ContainSubSlice](#ContainSubSlice)
|
||||
- [Chunk](#Chunk)
|
||||
- [Compact](#Compact)
|
||||
@@ -35,6 +36,9 @@ import (
|
||||
- [DifferenceWith](#DifferenceWith)
|
||||
- [DeleteAt](#DeleteAt)
|
||||
- [Drop](#Drop)
|
||||
- [DropRight](#DropRight)
|
||||
- [DropWhile](#DropWhile)
|
||||
- [DropRightWhile](#DropRightWhile)
|
||||
- [Every](#Every)
|
||||
- [Equal](#Equal)
|
||||
- [EqualWith](#EqualWith)
|
||||
@@ -44,6 +48,7 @@ import (
|
||||
- [Flatten](#Flatten)
|
||||
- [FlattenDeep](#FlattenDeep)
|
||||
- [ForEach](#ForEach)
|
||||
- [ForEachWithBreak](#ForEachWithBreak)
|
||||
- [GroupBy](#GroupBy)
|
||||
- [GroupWith](#GroupWith)
|
||||
- [IntSlice<sup>deprecated</sup>](#IntSlice)
|
||||
@@ -53,6 +58,8 @@ import (
|
||||
- [IndexOf](#IndexOf)
|
||||
- [LastIndexOf](#LastIndexOf)
|
||||
- [Map](#Map)
|
||||
- [FilterMap](#FilterMap)
|
||||
- [FlatMap](#FlatMap)
|
||||
- [Merge](#Merge)
|
||||
- [Reverse](#Reverse)
|
||||
- [Reduce](#Reduce)
|
||||
@@ -60,6 +67,10 @@ import (
|
||||
- [ReplaceAll](#ReplaceAll)
|
||||
- [Repeat](#Repeat)
|
||||
- [Shuffle](#Shuffle)
|
||||
- [IsAscending](#IsAscending)
|
||||
- [IsDescending](#IsDescending)
|
||||
- [IsSorted](#IsSorted)
|
||||
- [IsSortedByKey](#IsSortedByKey)
|
||||
- [Sort](#Sort)
|
||||
- [SortBy](#SortBy)
|
||||
- [SortByField<sup>deprecated</sup>](#SortByField)
|
||||
@@ -142,6 +153,51 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ContainBy">ContainBy</span>
|
||||
|
||||
<p>根据predicate函数判断切片是否包含某个值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ContainBy[T any](slice []T, predicate func(item T) bool) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type foo struct {
|
||||
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 })
|
||||
|
||||
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)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ContainSubSlice">ContainSubSlice</span>
|
||||
|
||||
<p>判断slice是否包含subslice</p>
|
||||
@@ -488,7 +544,7 @@ func main() {
|
||||
|
||||
### <span id="Drop">Drop</span>
|
||||
|
||||
<p>创建一个切片,当n > 0时从开头删除n个元素,或者当n < 0时从结尾删除n个元素</p>
|
||||
<p>从切片的头部删除n个元素。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -518,11 +574,130 @@ func main() {
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [b c]
|
||||
// [a b]
|
||||
// [a b c]
|
||||
// []
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DropRight">DropRight</span>
|
||||
|
||||
<p>从切片的尾部删除n个元素。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DropRight[T any](slice []T, n int) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [a b]
|
||||
// [a b c]
|
||||
// []
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DropWhile">DropWhile</span>
|
||||
|
||||
<p>从切片的头部删除n个元素,这个n个元素满足predicate函数返回true。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DropWhile[T any](slice []T, predicate func(item T) bool) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [2 3 4 5]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DropRightWhile">DropRightWhile</span>
|
||||
|
||||
<p>从切片的尾部删除n个元素,这个n个元素满足predicate函数返回true。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DropRightWhile[T any](slice []T, predicate func(item T) bool) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Every">Every</span>
|
||||
|
||||
<p>如果切片中的所有值都通过谓词函数,则返回true。 函数签名应该是func(index int, value any) bool</p>
|
||||
@@ -828,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>
|
||||
@@ -1127,6 +1340,76 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FilterMap">FilterMap</span>
|
||||
|
||||
<p>返回一个将filter和map操作应用于给定切片的切片。 iteratee回调函数应该返回两个值:1,结果值。2,结果值是否应该被包含在返回的切片中。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func FilterMap[T any, U any](slice []T, iteratee func(index int, item T) (U, bool)) []U
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
result := slice.FilterMap(nums, getEvenNumStr)
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
|
||||
// Output:
|
||||
// []string{"2", "4"}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FlatMap">FlatMap</span>
|
||||
|
||||
<p>将切片转换为其它类型切片。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func FlatMap[T any, U any](slice []T, iteratee func(index int, item T) []U) []U
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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}
|
||||
})
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
|
||||
// Output:
|
||||
// []string{"hi-1", "hi-2", "hi-3", "hi-4"}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Merge">Merge</span>
|
||||
|
||||
<p>合并多个切片(不会消除重复元素).</p>
|
||||
@@ -1342,13 +1625,155 @@ 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)
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsAscending">IsAscending</span>
|
||||
|
||||
<p>检查切片元素是否按升序排列。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsAscending[T constraints.Ordered](slice []T) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsDescending">IsDescending</span>
|
||||
|
||||
<p>检查切片元素是否按降序排列。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsDescending[T constraints.Ordered](slice []T) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsSorted">IsSorted</span>
|
||||
|
||||
<p>检查切片元素是否是有序的(升序或降序)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsSorted[T constraints.Ordered](slice []T) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="IsSortedByKey">IsSortedByKey</span>
|
||||
|
||||
<p>通过iteratee函数,检查切片元素是否是有序的。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func IsSortedByKey[T any, K constraints.Ordered](slice []T, iteratee func(item T) K) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Sort">Sort</span>
|
||||
|
||||
<p>对任何有序类型(数字或字符串)的切片进行排序,使用快速排序算法。 默认排序顺序为升序 (asc),如果需要降序,请将参数 `sortOrder` 设置为 `desc`。 Ordered类型:数字(所有整数浮点数)或字符串。</p>
|
||||
|
||||
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
|
||||
}
|
||||
```
|
||||
170
docs/strutil.md
170
docs/strutil.md
@@ -33,8 +33,9 @@ import (
|
||||
- [UpperKebabCase](#UpperKebabCase)
|
||||
- [LowerFirst](#LowerFirst)
|
||||
- [UpperFirst](#UpperFirst)
|
||||
- [PadEnd](#PadEnd)
|
||||
- [Pad](#Pad)
|
||||
- [PadStart](#PadStart)
|
||||
- [PadEnd](#PadEnd)
|
||||
- [Reverse](#Reverse)
|
||||
- [SnakeCase](#SnakeCase)
|
||||
- [UpperSnakeCase](#UpperSnakeCase)
|
||||
@@ -42,6 +43,9 @@ import (
|
||||
- [Substring](#Substring)
|
||||
- [Wrap](#Wrap)
|
||||
- [Unwrap](#Unwrap)
|
||||
- [SplitWords](#SplitWords)
|
||||
- [WordCount](#WordCount)
|
||||
- [RemoveNonPrintable](#RemoveNonPrintable)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -449,6 +453,51 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Pad">Pad</span>
|
||||
|
||||
<p>Pads string on the left and right side if it's shorter than size.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Pad(source string, size int, padStr string) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := strutil.Pad("foo", 1, "bar")
|
||||
result2 := strutil.Pad("foo", 2, "bar")
|
||||
result3 := strutil.Pad("foo", 3, "bar")
|
||||
result4 := strutil.Pad("foo", 4, "bar")
|
||||
result5 := strutil.Pad("foo", 5, "bar")
|
||||
result6 := strutil.Pad("foo", 6, "bar")
|
||||
result7 := strutil.Pad("foo", 7, "bar")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
// Output:
|
||||
// foo
|
||||
// foo
|
||||
// foo
|
||||
// foob
|
||||
// bfoob
|
||||
// bfooba
|
||||
// bafooba
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="PadEnd">PadEnd</span>
|
||||
|
||||
<p>Pads string on the right side if it's shorter than size.</p>
|
||||
@@ -802,3 +851,122 @@ func main() {
|
||||
// *foo*
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="SplitWords">SplitWords</span>
|
||||
|
||||
<p>Splits a string into words, word only contains alphabetic characters.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func SplitWords(s string) []string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := strutil.SplitWords("a word")
|
||||
result2 := strutil.SplitWords("I'am a programmer")
|
||||
result3 := strutil.SplitWords("Bonjour, je suis programmeur")
|
||||
result4 := strutil.SplitWords("a -b-c' 'd'e")
|
||||
result5 := strutil.SplitWords("你好,我是一名码农")
|
||||
result6 := strutil.SplitWords("こんにちは,私はプログラマーです")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// [a word]
|
||||
// [I'am a programmer]
|
||||
// [Bonjour je suis programmeur]
|
||||
// [a b-c' d'e]
|
||||
// []
|
||||
// []
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="WordCount">WordCount</span>
|
||||
|
||||
<p>Return the number of meaningful word, word only contains alphabetic characters.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func WordCount(s string) int
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := strutil.WordCount("a word")
|
||||
result2 := strutil.WordCount("I'am a programmer")
|
||||
result3 := strutil.WordCount("Bonjour, je suis programmeur")
|
||||
result4 := strutil.WordCount("a -b-c' 'd'e")
|
||||
result5 := strutil.WordCount("你好,我是一名码农")
|
||||
result6 := strutil.WordCount("こんにちは,私はプログラマーです")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 3
|
||||
// 4
|
||||
// 3
|
||||
// 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
|
||||
// 你好😄
|
||||
}
|
||||
```
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
- [UpperKebabCase](#UpperKebabCase)
|
||||
- [LowerFirst](#LowerFirst)
|
||||
- [UpperFirst](#UpperFirst)
|
||||
- [Pad](#Pad)
|
||||
- [PadEnd](#PadEnd)
|
||||
- [PadStart](#PadStart)
|
||||
- [Reverse](#Reverse)
|
||||
@@ -42,6 +43,9 @@ import (
|
||||
- [Substring](#Substring)
|
||||
- [Wrap](#Wrap)
|
||||
- [Unwrap](#Unwrap)
|
||||
- [SplitWords](#SplitWords)
|
||||
- [WordCount](#WordCount)
|
||||
- [RemoveNonPrintable](#RemoveNonPrintable)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -449,6 +453,51 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Pad">Pad</span>
|
||||
|
||||
<p>如果字符串长度短于size,则在左右两侧填充字符串。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Pad(source string, size int, padStr string) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := strutil.Pad("foo", 1, "bar")
|
||||
result2 := strutil.Pad("foo", 2, "bar")
|
||||
result3 := strutil.Pad("foo", 3, "bar")
|
||||
result4 := strutil.Pad("foo", 4, "bar")
|
||||
result5 := strutil.Pad("foo", 5, "bar")
|
||||
result6 := strutil.Pad("foo", 6, "bar")
|
||||
result7 := strutil.Pad("foo", 7, "bar")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
// Output:
|
||||
// foo
|
||||
// foo
|
||||
// foo
|
||||
// foob
|
||||
// bfoob
|
||||
// bfooba
|
||||
// bafooba
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="PadEnd">PadEnd</span>
|
||||
|
||||
<p>如果字符串长度短于size,则在右侧填充字符串。</p>
|
||||
@@ -802,3 +851,121 @@ func main() {
|
||||
// *foo*
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="SplitWords">SplitWords</span>
|
||||
|
||||
<p>将字符串拆分为单词,只支持字母字符单词。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func SplitWords(s string) []string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := strutil.SplitWords("a word")
|
||||
result2 := strutil.SplitWords("I'am a programmer")
|
||||
result3 := strutil.SplitWords("Bonjour, je suis programmeur")
|
||||
result4 := strutil.SplitWords("a -b-c' 'd'e")
|
||||
result5 := strutil.SplitWords("你好,我是一名码农")
|
||||
result6 := strutil.SplitWords("こんにちは,私はプログラマーです")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// [a word]
|
||||
// [I'am a programmer]
|
||||
// [Bonjour je suis programmeur]
|
||||
// [a b-c' d'e]
|
||||
// []
|
||||
// []
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="WordCount">WordCount</span>
|
||||
|
||||
<p>返回有意义单词的数量,只支持字母字符单词。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func WordCount(s string) int
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := strutil.WordCount("a word")
|
||||
result2 := strutil.WordCount("I'am a programmer")
|
||||
result3 := strutil.WordCount("Bonjour, je suis programmeur")
|
||||
result4 := strutil.WordCount("a -b-c' 'd'e")
|
||||
result5 := strutil.WordCount("你好,我是一名码农")
|
||||
result6 := strutil.WordCount("こんにちは,私はプログラマーです")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 3
|
||||
// 4
|
||||
// 3
|
||||
// 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
|
||||
}
|
||||
```
|
||||
430
docs/xerror.md
430
docs/xerror.md
@@ -22,20 +22,40 @@ import (
|
||||
|
||||
## Index
|
||||
|
||||
- [New](#New)
|
||||
- [Wrap](#Wrap)
|
||||
- [Unwrap](#Unwrap)
|
||||
- [XError_Wrap](#XError_Wrap)
|
||||
- [XError_Unwrap](#XError_Unwrap)
|
||||
- [XError_With](#XError_With)
|
||||
- [XError_Is](#XError_Is)
|
||||
- [XError_Id](#XError_Id)
|
||||
- [XError_Values](#XError_Values)
|
||||
- [XError_StackTrace](#XError_StackTrace)
|
||||
- [XError_Info](#XError_Info)
|
||||
- [XError_Error](#XError_Error)
|
||||
- [TryUnwrap](#TryUnwrap)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="Unwrap">Unwrap</span>
|
||||
### <span id="New">New</span>
|
||||
|
||||
<p>Unwrap if err is nil then it returns a valid value. If err is not nil, Unwrap panics with err.</p>
|
||||
<p>Creates a new XError pointer instance with message.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Unwrap[T any](val T, err error) T
|
||||
type XError struct {
|
||||
id string
|
||||
message string
|
||||
stack *stack
|
||||
cause error
|
||||
values map[string]any
|
||||
}
|
||||
|
||||
func New(format string, args ...any) *XError
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
@@ -49,7 +69,407 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := xerror.Unwrap(strconv.Atoi("42"))
|
||||
err := xerror.New("error")
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
// error
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Wrap">Wrap</span>
|
||||
|
||||
<p>Creates a new XError pointer instance based on error object, and add message.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Wrap(cause error, message ...any) *XError
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("wrong password")
|
||||
wrapErr := xerror.Wrap(err, "error")
|
||||
|
||||
fmt.Println(wrapErr.Error())
|
||||
|
||||
// Output:
|
||||
// error: wrong password
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Unwrap">Unwrap</span>
|
||||
|
||||
<p>Returns unwrapped XError from err by errors.As. If no XError, returns nil.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Unwrap(err error) *XError
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err1 := xerror.New("error").With("level", "high")
|
||||
wrapErr := errors.Wrap(err1, "oops")
|
||||
|
||||
err := xerror.Unwrap(wrapErr)
|
||||
|
||||
values := err.Values()
|
||||
fmt.Println(values["level"])
|
||||
|
||||
// Output:
|
||||
// high
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_Wrap">XError_Wrap</span>
|
||||
|
||||
<p>Creates a new XError and copy message and id to new one.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Wrap(cause error) *XError
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("wrong password")
|
||||
wrapErr := xerror.Wrap(err, "error")
|
||||
|
||||
fmt.Println(wrapErr.Error())
|
||||
|
||||
// Output:
|
||||
// error: wrong password
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_Unwrap">XError_Unwrap</span>
|
||||
|
||||
<p>Compatible with github.com/pkg/errors.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Unwrap() error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err1 := xerror.New("error").With("level", "high")
|
||||
err2 := err1.Wrap(errors.New("invalid username"))
|
||||
|
||||
err := err2.Unwrap()
|
||||
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
// invalid username
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_With">XError_With</span>
|
||||
|
||||
<p>Adds key and value related to the XError object.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) With(key string, value any) *XError
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("error").With("level", "high")
|
||||
|
||||
errLevel := err.Values()["level"]
|
||||
|
||||
fmt.Println(errLevel)
|
||||
|
||||
// Output:
|
||||
// high
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_Id">XError_Id</span>
|
||||
|
||||
<p>Sets XError object id to check equality in XError.Is.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Id(id string) *XError
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err1 := xerror.New("error").Id("e001")
|
||||
err2 := xerror.New("error").Id("e001")
|
||||
err3 := xerror.New("error").Id("e003")
|
||||
|
||||
equal := err1.Is(err2)
|
||||
notEqual := err1.Is(err3)
|
||||
|
||||
fmt.Println(equal)
|
||||
fmt.Println(notEqual)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_Is">XError_Is</span>
|
||||
|
||||
<p>Checks if target error is XError and Error.id of two errors are matched.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Is(target error) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err1 := xerror.New("error").Id("e001")
|
||||
err2 := xerror.New("error").Id("e001")
|
||||
err3 := xerror.New("error").Id("e003")
|
||||
|
||||
equal := err1.Is(err2)
|
||||
notEqual := err1.Is(err3)
|
||||
|
||||
fmt.Println(equal)
|
||||
fmt.Println(notEqual)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_Values">XError_Values</span>
|
||||
|
||||
<p>Returns map of key and value that is set by With. All wrapped xerror.XError key and values will be merged. Key and values of wrapped error is overwritten by upper xerror.XError.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Values() map[string]any
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("error").With("level", "high")
|
||||
|
||||
errLevel := err.Values()["level"]
|
||||
|
||||
fmt.Println(errLevel)
|
||||
|
||||
// Output:
|
||||
// high
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="XError_StackTrace">XError_StackTrace</span>
|
||||
|
||||
<p>Returns stack trace which is compatible with pkg/errors.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) StackTrace() StackTrace
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("error")
|
||||
|
||||
stacks := err.Stacks()
|
||||
|
||||
fmt.Println(stacks[0].Func)
|
||||
fmt.Println(stacks[0].Line)
|
||||
|
||||
containFile := strings.Contains(stacks[0].File, "xxx.go")
|
||||
fmt.Println(containFile)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="XError_Info">XError_Info</span>
|
||||
|
||||
<p>Returns information of xerror, which can be printed.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Info() *errInfo
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cause := errors.New("error")
|
||||
err := xerror.Wrap(cause, "invalid username").Id("e001").With("level", "high")
|
||||
|
||||
errInfo := err.Info()
|
||||
|
||||
fmt.Println(errInfo.Id)
|
||||
fmt.Println(errInfo.Cause)
|
||||
fmt.Println(errInfo.Values["level"])
|
||||
fmt.Println(errInfo.Message)
|
||||
|
||||
// Output:
|
||||
// e001
|
||||
// error
|
||||
// high
|
||||
// invalid username
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="XError_Error">XError_Error</span>
|
||||
|
||||
<p>Error implements standard error interface.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Error() string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("error")
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
// error
|
||||
}
|
||||
```
|
||||
### <span id="TryUnwrap">TryUnwrap</span>
|
||||
|
||||
<p>TryUnwrap if err is nil then it returns a valid value. If err is not nil, Unwrap panics with err.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func TryUnwrap[T any](val T, err error) T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := xerror.TryUnwrap(strconv.Atoi("42"))
|
||||
fmt.Println(result1)
|
||||
|
||||
_, err := strconv.Atoi("4o2")
|
||||
@@ -59,7 +479,7 @@ func main() {
|
||||
fmt.Println(result2)
|
||||
}()
|
||||
|
||||
xerror.Unwrap(strconv.Atoi("4o2"))
|
||||
xerror.TryUnwrap(strconv.Atoi("4o2"))
|
||||
|
||||
// Output:
|
||||
// 42
|
||||
|
||||
@@ -22,20 +22,40 @@ import (
|
||||
|
||||
## 目录
|
||||
|
||||
- [New](#New)
|
||||
- [Wrap](#Wrap)
|
||||
- [Unwrap](#Unwrap)
|
||||
- [XError_Wrap](#XError_Wrap)
|
||||
- [XError_Unwrap](#XError_Unwrap)
|
||||
- [XError_With](#XError_With)
|
||||
- [XError_Is](#XError_Is)
|
||||
- [XError_Id](#XError_Id)
|
||||
- [XError_Values](#XError_Values)
|
||||
- [XError_StackTrace](#XError_StackTrace)
|
||||
- [XError_Info](#XError_Info)
|
||||
- [XError_Error](#XError_Error)
|
||||
- [TryUnwrap](#TryUnwrap)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="Unwrap">Unwrap</span>
|
||||
### <span id="New">New</span>
|
||||
|
||||
<p>检查error, 如果err为nil则展开,则它返回一个有效值,如果err不是nil则Unwrap使用err发生panic。</p>
|
||||
<p>创建XError对象实例。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Unwrap[T any](val T, err error) T
|
||||
type XError struct {
|
||||
id string
|
||||
message string
|
||||
stack *stack
|
||||
cause error
|
||||
values map[string]any
|
||||
}
|
||||
|
||||
func New(format string, args ...any) *XError
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
@@ -49,7 +69,408 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := xerror.Unwrap(strconv.Atoi("42"))
|
||||
err := xerror.New("error")
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
// error
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Wrap">Wrap</span>
|
||||
|
||||
<p>根据error对象创建XError对象实例,可添加message。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Wrap(cause error, message ...any) *XError
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("wrong password")
|
||||
wrapErr := xerror.Wrap(err, "error")
|
||||
|
||||
fmt.Println(wrapErr.Error())
|
||||
|
||||
// Output:
|
||||
// error: wrong password
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Unwrap">Unwrap</span>
|
||||
|
||||
<p>从error对象中解构出XError。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Unwrap(err error) *XError
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err1 := xerror.New("error").With("level", "high")
|
||||
wrapErr := errors.Wrap(err1, "oops")
|
||||
|
||||
err := xerror.Unwrap(wrapErr)
|
||||
|
||||
values := err.Values()
|
||||
fmt.Println(values["level"])
|
||||
|
||||
// Output:
|
||||
// high
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_Wrap">XError_Wrap</span>
|
||||
|
||||
<p>创建新的XError对象并将消息和id复制到新的对象中。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Wrap(cause error) *XError
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("wrong password")
|
||||
wrapErr := xerror.Wrap(err, "error")
|
||||
|
||||
fmt.Println(wrapErr.Error())
|
||||
|
||||
// Output:
|
||||
// error: wrong password
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_Unwrap">XError_Unwrap</span>
|
||||
|
||||
<p>解构XEerror为error对象。适配github.com/pkg/errors。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Unwrap() error
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err1 := xerror.New("error").With("level", "high")
|
||||
err2 := err1.Wrap(errors.New("invalid username"))
|
||||
|
||||
err := err2.Unwrap()
|
||||
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
// invalid username
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_With">XError_With</span>
|
||||
|
||||
<p>添加与XError对象的键和值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) With(key string, value any) *XError
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("error").With("level", "high")
|
||||
|
||||
errLevel := err.Values()["level"]
|
||||
|
||||
fmt.Println(errLevel)
|
||||
|
||||
// Output:
|
||||
// high
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_Id">XError_Id</span>
|
||||
|
||||
<p>设置XError对象的id。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Id(id string) *XError
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err1 := xerror.New("error").Id("e001")
|
||||
err2 := xerror.New("error").Id("e001")
|
||||
err3 := xerror.New("error").Id("e003")
|
||||
|
||||
equal := err1.Is(err2)
|
||||
notEqual := err1.Is(err3)
|
||||
|
||||
fmt.Println(equal)
|
||||
fmt.Println(notEqual)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_Is">XError_Is</span>
|
||||
|
||||
<p>检查目标error是否为XError,两个错误中的error.id是否匹配。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Is(target error) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err1 := xerror.New("error").Id("e001")
|
||||
err2 := xerror.New("error").Id("e001")
|
||||
err3 := xerror.New("error").Id("e003")
|
||||
|
||||
equal := err1.Is(err2)
|
||||
notEqual := err1.Is(err3)
|
||||
|
||||
fmt.Println(equal)
|
||||
fmt.Println(notEqual)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="XError_Values">XError_Values</span>
|
||||
|
||||
<p>返回由With设置的键和值的映射。将合并所有XError键和值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Values() map[string]any
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("error").With("level", "high")
|
||||
|
||||
errLevel := err.Values()["level"]
|
||||
|
||||
fmt.Println(errLevel)
|
||||
|
||||
// Output:
|
||||
// high
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="XError_StackTrace">XError_StackTrace</span>
|
||||
|
||||
<p>返回与pkg/error兼容的堆栈信息。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) StackTrace() StackTrace
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("error")
|
||||
|
||||
stacks := err.Stacks()
|
||||
|
||||
fmt.Println(stacks[0].Func)
|
||||
fmt.Println(stacks[0].Line)
|
||||
|
||||
containFile := strings.Contains(stacks[0].File, "xxx.go")
|
||||
fmt.Println(containFile)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="XError_Info">XError_Info</span>
|
||||
|
||||
<p>返回可打印的XError对象信息。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Info() *errInfo
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cause := errors.New("error")
|
||||
err := xerror.Wrap(cause, "invalid username").Id("e001").With("level", "high")
|
||||
|
||||
errInfo := err.Info()
|
||||
|
||||
fmt.Println(errInfo.Id)
|
||||
fmt.Println(errInfo.Cause)
|
||||
fmt.Println(errInfo.Values["level"])
|
||||
fmt.Println(errInfo.Message)
|
||||
|
||||
// Output:
|
||||
// e001
|
||||
// error
|
||||
// high
|
||||
// invalid username
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="XError_Error">XError_Error</span>
|
||||
|
||||
<p>实现标准库的error接口。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (e *XError) Error() string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := xerror.New("error")
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
// error
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="TryUnwrap">TryUnwrap</span>
|
||||
|
||||
<p>检查error, 如果err为nil则展开,则它返回一个有效值,如果err不是nil则TryUnwrap使用err发生panic。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func TryUnwrap[T any](val T, err error) T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/xerror"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := xerror.TryUnwrap(strconv.Atoi("42"))
|
||||
fmt.Println(result1)
|
||||
|
||||
_, err := strconv.Atoi("4o2")
|
||||
@@ -59,7 +480,7 @@ func main() {
|
||||
fmt.Println(result2)
|
||||
}()
|
||||
|
||||
xerror.Unwrap(strconv.Atoi("4o2"))
|
||||
xerror.TryUnwrap(strconv.Atoi("4o2"))
|
||||
|
||||
// Output:
|
||||
// 42
|
||||
|
||||
@@ -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
|
||||
}
|
||||
165
maputil/map.go
165
maputil/map.go
@@ -4,7 +4,11 @@
|
||||
// Package maputil includes some functions to manipulate map.
|
||||
package maputil
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
// Keys returns a slice of the map's keys.
|
||||
// Play: https://go.dev/play/p/xNB5bTb97Wd
|
||||
@@ -34,6 +38,30 @@ func Values[K comparable, V any](m map[K]V) []V {
|
||||
return values
|
||||
}
|
||||
|
||||
// KeysBy creates a slice whose element is the result of function mapper invoked by every map's key.
|
||||
// Play: https://go.dev/play/p/hI371iB8Up8
|
||||
func KeysBy[K comparable, V any, T any](m map[K]V, mapper func(item K) T) []T {
|
||||
keys := make([]T, 0, len(m))
|
||||
|
||||
for k := range m {
|
||||
keys = append(keys, mapper(k))
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
// ValuesBy creates a slice whose element is the result of function mapper invoked by every map's value.
|
||||
// Play: https://go.dev/play/p/sg9-oRidh8f
|
||||
func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T {
|
||||
keys := make([]T, 0, len(m))
|
||||
|
||||
for _, v := range m {
|
||||
keys = append(keys, mapper(v))
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
// Merge maps, next key will overwrite previous key.
|
||||
// Play: https://go.dev/play/p/H95LENF1uB-
|
||||
func Merge[K comparable, V any](maps ...map[K]V) map[K]V {
|
||||
@@ -69,6 +97,71 @@ func Filter[K comparable, V any](m map[K]V, predicate func(key K, value V) bool)
|
||||
return result
|
||||
}
|
||||
|
||||
// FilterByKeys iterates over map, return a new map whose keys are all given keys.
|
||||
// Play: https://go.dev/play/p/7ov6BJHbVqh
|
||||
func FilterByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V {
|
||||
result := make(map[K]V)
|
||||
|
||||
for k, v := range m {
|
||||
if slice.Contain(keys, k) {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// FilterByValues iterates over map, return a new map whose values are all given values.
|
||||
// Play: https://go.dev/play/p/P3-9MdcXegR
|
||||
func FilterByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V {
|
||||
result := make(map[K]V)
|
||||
|
||||
for k, v := range m {
|
||||
if slice.Contain(values, v) {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// OmitBy is the opposite of Filter, removes all the map elements for which the predicate function returns true.
|
||||
// Play: https://go.dev/play/p/YJM4Hj5hNwm
|
||||
func OmitBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) map[K]V {
|
||||
result := make(map[K]V)
|
||||
|
||||
for k, v := range m {
|
||||
if !predicate(k, v) {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// OmitByKeys the opposite of FilterByKeys, extracts all the map elements which keys are not omitted.
|
||||
// Play: https://go.dev/play/p/jXGrWDBfSRp
|
||||
func OmitByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V {
|
||||
result := make(map[K]V)
|
||||
|
||||
for k, v := range m {
|
||||
if !slice.Contain(keys, k) {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// OmitByValues the opposite of FilterByValues. remov all elements whose value are in the give slice.
|
||||
// Play: https://go.dev/play/p/XB7Y10uw20_U
|
||||
func OmitByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V {
|
||||
result := make(map[K]V)
|
||||
|
||||
for k, v := range m {
|
||||
if !slice.Contain(values, v) {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersect iterates over maps, return a new map of key and value pairs in all given maps.
|
||||
// Play: https://go.dev/play/p/Zld0oj3sjcC
|
||||
func Intersect[K comparable, V any](maps ...map[K]V) map[K]V {
|
||||
@@ -126,3 +219,73 @@ func IsDisjoint[K comparable, V any](mapA, mapB map[K]V) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Entry is a key/value pairs.
|
||||
type Entry[K comparable, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
|
||||
// Entries transforms a map into array of key/value pairs.
|
||||
// Play: https://go.dev/play/p/Ltb11LNcElY
|
||||
func Entries[K comparable, V any](m map[K]V) []Entry[K, V] {
|
||||
entries := make([]Entry[K, V], 0, len(m))
|
||||
|
||||
for k, v := range m {
|
||||
entries = append(entries, Entry[K, V]{
|
||||
Key: k,
|
||||
Value: v,
|
||||
})
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
// FromEntries creates a map based on a slice of key/value pairs
|
||||
// Play: https://go.dev/play/p/fTdu4sCNjQO
|
||||
func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V {
|
||||
result := make(map[K]V, len(entries))
|
||||
|
||||
for _, v := range entries {
|
||||
result[v.Key] = v.Value
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Transform a map to another type map.
|
||||
// Play: https://go.dev/play/p/P6ovfToM3zj
|
||||
func Transform[K1 comparable, V1 any, K2 comparable, V2 any](m map[K1]V1, iteratee func(key K1, value V1) (K2, V2)) map[K2]V2 {
|
||||
result := make(map[K2]V2, len(m))
|
||||
|
||||
for k1, v1 := range m {
|
||||
k2, v2 := iteratee(k1, v1)
|
||||
result[k2] = v2
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// MapKeys transforms a map to other type map by manipulating it's keys.
|
||||
// Play: https://go.dev/play/p/8scDxWeBDKd
|
||||
func MapKeys[K comparable, V any, T comparable](m map[K]V, iteratee func(key K, value V) T) map[T]V {
|
||||
result := make(map[T]V, len(m))
|
||||
|
||||
for k, v := range m {
|
||||
result[iteratee(k, v)] = v
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// MapValues transforms a map to other type map by manipulating it's values.
|
||||
// Play: https://go.dev/play/p/g92aY3fc7Iw
|
||||
func MapValues[K comparable, V any, T any](m map[K]V, iteratee func(key K, value V) T) map[K]T {
|
||||
result := make(map[K]T, len(m))
|
||||
|
||||
for k, v := range m {
|
||||
result[k] = iteratee(k, v)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package maputil
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func ExampleKeys() {
|
||||
@@ -41,6 +42,51 @@ func ExampleValues() {
|
||||
// [a a b c d]
|
||||
}
|
||||
|
||||
func ExampleKeysBy() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "a",
|
||||
3: "b",
|
||||
}
|
||||
|
||||
keys := KeysBy(m, func(n int) int {
|
||||
return n + 1
|
||||
})
|
||||
|
||||
sort.Ints(keys)
|
||||
|
||||
fmt.Println(keys)
|
||||
|
||||
// Output:
|
||||
// [2 3 4]
|
||||
}
|
||||
|
||||
func ExampleValuesBy() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
values := ValuesBy(m, func(v string) string {
|
||||
switch v {
|
||||
case "a":
|
||||
return "a-1"
|
||||
case "b":
|
||||
return "b-2"
|
||||
case "c":
|
||||
return "c-3"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
})
|
||||
|
||||
sort.Strings(values)
|
||||
|
||||
fmt.Println(values)
|
||||
|
||||
// Output:
|
||||
// [a-1 b-2 c-3]
|
||||
}
|
||||
func ExampleMerge() {
|
||||
m1 := map[int]string{
|
||||
1: "a",
|
||||
@@ -99,6 +145,40 @@ func ExampleFilter() {
|
||||
// map[b:2 d:4]
|
||||
}
|
||||
|
||||
func ExampleFilterByKeys() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := FilterByKeys(m, []string{"a", "b"})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2]
|
||||
}
|
||||
|
||||
func ExampleFilterByValues() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := FilterByValues(m, []int{3, 4})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[c:3 d:4]
|
||||
}
|
||||
|
||||
func ExampleIntersect() {
|
||||
m1 := map[string]int{
|
||||
"a": 1,
|
||||
@@ -179,3 +259,141 @@ func ExampleIsDisjoint() {
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleEntries() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
|
||||
result := Entries(m)
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].Value < result[j].Value
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// [{a 1} {b 2} {c 3}]
|
||||
}
|
||||
|
||||
func ExampleFromEntries() {
|
||||
|
||||
result := FromEntries([]Entry[string, int]{
|
||||
{Key: "a", Value: 1},
|
||||
{Key: "b", Value: 2},
|
||||
{Key: "c", Value: 3},
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
|
||||
func ExampleTransform() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
|
||||
result := Transform(m, func(k string, v int) (string, string) {
|
||||
return k, strconv.Itoa(v)
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
|
||||
func ExampleMapKeys() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
|
||||
result := MapKeys(m, func(k int, _ string) string {
|
||||
return strconv.Itoa(k)
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[1:a 2:b 3:c]
|
||||
}
|
||||
|
||||
func ExampleMapValues() {
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
|
||||
result := MapValues(m, func(k int, v string) string {
|
||||
return v + strconv.Itoa(k)
|
||||
})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[1:a1 2:b2 3:c3]
|
||||
}
|
||||
|
||||
func ExampleOmitBy() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
isEven := func(_ string, value int) bool {
|
||||
return value%2 == 0
|
||||
}
|
||||
|
||||
result := OmitBy(m, isEven)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 c:3 e:5]
|
||||
}
|
||||
|
||||
func ExampleOmitByKeys() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := OmitByKeys(m, []string{"a", "b"})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[c:3 d:4 e:5]
|
||||
}
|
||||
|
||||
func ExampleOmitByValues() {
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
result := OmitByValues(m, []int{4, 5})
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package maputil
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
@@ -41,6 +42,51 @@ func TestValues(t *testing.T) {
|
||||
assert.Equal([]string{"a", "a", "b", "c", "d"}, values)
|
||||
}
|
||||
|
||||
func TestKeysBy(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestKeysBy")
|
||||
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "a",
|
||||
3: "b",
|
||||
}
|
||||
|
||||
keys := KeysBy(m, func(n int) int {
|
||||
return n + 1
|
||||
})
|
||||
|
||||
sort.Ints(keys)
|
||||
|
||||
assert.Equal([]int{2, 3, 4}, keys)
|
||||
}
|
||||
|
||||
func TestValuesBy(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestValuesBy")
|
||||
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
|
||||
values := ValuesBy(m, func(v string) string {
|
||||
switch v {
|
||||
case "a":
|
||||
return "a-1"
|
||||
case "b":
|
||||
return "b-2"
|
||||
case "c":
|
||||
return "c-3"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
})
|
||||
|
||||
sort.Strings(values)
|
||||
|
||||
assert.Equal([]string{"a-1", "b-2", "c-3"}, values)
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestMerge")
|
||||
|
||||
@@ -104,6 +150,107 @@ func TestFilter(t *testing.T) {
|
||||
}, acturl)
|
||||
}
|
||||
|
||||
func TestFilterByKeys(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestFilterByKeys")
|
||||
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
acturl := FilterByKeys(m, []string{"a", "b"})
|
||||
|
||||
assert.Equal(map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}, acturl)
|
||||
}
|
||||
|
||||
func TestFilterByValues(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestFilterByValues")
|
||||
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
acturl := FilterByValues(m, []int{3, 4})
|
||||
|
||||
assert.Equal(map[string]int{
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
}, acturl)
|
||||
}
|
||||
|
||||
func TestOmitBy(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestOmitBy")
|
||||
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
isEven := func(_ string, value int) bool {
|
||||
return value%2 == 0
|
||||
}
|
||||
|
||||
acturl := OmitBy(m, isEven)
|
||||
|
||||
assert.Equal(map[string]int{
|
||||
"a": 1,
|
||||
"c": 3,
|
||||
"e": 5,
|
||||
}, acturl)
|
||||
}
|
||||
|
||||
func TestOmitByKeys(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestOmitByKeys")
|
||||
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
acturl := OmitByKeys(m, []string{"a", "b"})
|
||||
|
||||
assert.Equal(map[string]int{
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}, acturl)
|
||||
}
|
||||
|
||||
func TestOmitByValues(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestOmitByValues")
|
||||
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
"d": 4,
|
||||
"e": 5,
|
||||
}
|
||||
|
||||
acturl := OmitByValues(m, []int{4, 5})
|
||||
|
||||
assert.Equal(map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}, acturl)
|
||||
}
|
||||
|
||||
func TestIntersect(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIntersect")
|
||||
|
||||
@@ -170,3 +317,104 @@ func TestIsDisjoint(t *testing.T) {
|
||||
|
||||
assert.Equal(false, IsDisjoint(m1, m3))
|
||||
}
|
||||
|
||||
func TestEntries(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEntries")
|
||||
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
|
||||
result := Entries(m)
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].Value < result[j].Value
|
||||
})
|
||||
|
||||
expected := []Entry[string, int]{{Key: "a", Value: 1}, {Key: "b", Value: 2}, {Key: "c", Value: 3}}
|
||||
|
||||
assert.Equal(expected, result)
|
||||
}
|
||||
|
||||
func TestFromEntries(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestFromEntries")
|
||||
|
||||
result := FromEntries([]Entry[string, int]{
|
||||
{Key: "a", Value: 1},
|
||||
{Key: "b", Value: 2},
|
||||
{Key: "c", Value: 3},
|
||||
})
|
||||
|
||||
assert.Equal(3, len(result))
|
||||
assert.Equal(1, result["a"])
|
||||
assert.Equal(2, result["b"])
|
||||
assert.Equal(3, result["c"])
|
||||
}
|
||||
|
||||
func TestTransform(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestTransform")
|
||||
|
||||
m := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3,
|
||||
}
|
||||
|
||||
result := Transform(m, func(k string, v int) (string, string) {
|
||||
return k, strconv.Itoa(v)
|
||||
})
|
||||
|
||||
expected := map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
"c": "3",
|
||||
}
|
||||
|
||||
assert.Equal(expected, result)
|
||||
}
|
||||
|
||||
func TestMapKeys(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestMapKeys")
|
||||
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
|
||||
result := MapKeys(m, func(k int, _ string) string {
|
||||
return strconv.Itoa(k)
|
||||
})
|
||||
|
||||
expected := map[string]string{
|
||||
"1": "a",
|
||||
"2": "b",
|
||||
"3": "c",
|
||||
}
|
||||
|
||||
assert.Equal(expected, result)
|
||||
}
|
||||
|
||||
func TestMapValues(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestMapKeys")
|
||||
|
||||
m := map[int]string{
|
||||
1: "a",
|
||||
2: "b",
|
||||
3: "c",
|
||||
}
|
||||
|
||||
result := MapValues(m, func(k int, v string) string {
|
||||
return v + strconv.Itoa(k)
|
||||
})
|
||||
|
||||
expected := map[int]string{
|
||||
1: "a1",
|
||||
2: "b2",
|
||||
3: "c3",
|
||||
}
|
||||
|
||||
assert.Equal(expected, result)
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
})
|
||||
|
||||
}
|
||||
@@ -13,12 +13,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
NUMERAL = "0123456789"
|
||||
LOWER_LETTERS = "abcdefghijklmnopqrstuvwxyz"
|
||||
UPPER_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
Numeral = "0123456789"
|
||||
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
|
||||
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// RandInt generate random int between min and max, maybe min, not be max.
|
||||
// Play: https://go.dev/play/p/pXyyAAI5YxD
|
||||
func RandInt(min, max int) int {
|
||||
@@ -28,9 +32,12 @@ func RandInt(min, max int) int {
|
||||
if max < min {
|
||||
min, max = max, min
|
||||
}
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
return r.Intn(max-min) + min
|
||||
// fix: https://github.com/duke-git/lancet/issues/75
|
||||
// r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
// return r.Intn(max-min) + min
|
||||
|
||||
return rand.Intn(max-min) + min
|
||||
}
|
||||
|
||||
// RandBytes generate random byte slice.
|
||||
@@ -51,40 +58,42 @@ func RandBytes(length int) []byte {
|
||||
// RandString generate random string of specified length.
|
||||
// Play: https://go.dev/play/p/W2xvRUXA7Mi
|
||||
func RandString(length int) string {
|
||||
return random(LETTERS, length)
|
||||
return random(Letters, length)
|
||||
}
|
||||
|
||||
// RandUpper generate a random upper case string.
|
||||
// Play: https://go.dev/play/p/29QfOh0DVuh
|
||||
func RandUpper(length int) string {
|
||||
return random(UPPER_LETTERS, length)
|
||||
return random(UpperLetters, length)
|
||||
}
|
||||
|
||||
// RandLower generate a random lower case string.
|
||||
// Play: https://go.dev/play/p/XJtZ471cmtI
|
||||
func RandLower(length int) string {
|
||||
return random(LOWER_LETTERS, length)
|
||||
return random(LowwerLetters, length)
|
||||
}
|
||||
|
||||
// RandNumeral generate a random numeral string of specified length.
|
||||
// Play: https://go.dev/play/p/g4JWVpHsJcf
|
||||
func RandNumeral(length int) string {
|
||||
return random(NUMERAL, length)
|
||||
return random(Numeral, length)
|
||||
}
|
||||
|
||||
// RandNumeralOrLetter generate a random numeral or letter string.
|
||||
// Play: https://go.dev/play/p/19CEQvpx2jD
|
||||
func RandNumeralOrLetter(length int) string {
|
||||
return random(NUMERAL+LETTERS, length)
|
||||
return random(Numeral+Letters, length)
|
||||
}
|
||||
|
||||
// random generate a random string based on given string range.
|
||||
func random(s string, length int) string {
|
||||
b := make([]byte, length)
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
// fix: https://github.com/duke-git/lancet/issues/75
|
||||
// r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
for i := range b {
|
||||
b[i] = s[r.Int63()%int64(len(s))]
|
||||
b[i] = s[rand.Int63()%int64(len(s))]
|
||||
}
|
||||
|
||||
return string(b)
|
||||
|
||||
181
slice/slice.go
181
slice/slice.go
@@ -6,7 +6,6 @@ package slice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"sort"
|
||||
@@ -34,6 +33,18 @@ func Contain[T comparable](slice []T, target T) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainBy returns true if predicate function return true.
|
||||
// Play: https://go.dev/play/p/49tkHfX4GNc
|
||||
func ContainBy[T any](slice []T, predicate func(item T) bool) bool {
|
||||
for _, item := range slice {
|
||||
if predicate(item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainSubSlice check if the slice contain a given subslice or not.
|
||||
// Play: https://go.dev/play/p/bcuQ3UT6Sev
|
||||
func ContainSubSlice[T comparable](slice, subSlice []T) bool {
|
||||
@@ -410,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 {
|
||||
@@ -422,6 +444,35 @@ func Map[T any, U any](slice []T, iteratee func(index int, item T) U) []U {
|
||||
return result
|
||||
}
|
||||
|
||||
// FilterMap returns a slice which apply both filtering and mapping to the given slice.
|
||||
// iteratee callback function should returntwo values:
|
||||
// 1, mapping result.
|
||||
// 2, whether the result element should be included or not
|
||||
// Play: https://go.dev/play/p/J94SZ_9MiIe
|
||||
func FilterMap[T any, U any](slice []T, iteratee func(index int, item T) (U, bool)) []U {
|
||||
result := []U{}
|
||||
|
||||
for i, v := range slice {
|
||||
if a, ok := iteratee(i, v); ok {
|
||||
result = append(result, a)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// FlatMap manipulates a slice and transforms and flattens it to a slice of another type.
|
||||
// Play: https://go.dev/play/p/_QARWlWs1N_F
|
||||
func FlatMap[T any, U any](slice []T, iteratee func(index int, item T) []U) []U {
|
||||
result := make([]U, 0, len(slice))
|
||||
|
||||
for i, v := range slice {
|
||||
result = append(result, iteratee(i, v)...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Reduce creates an slice of values by running each element of slice thru iteratee function.
|
||||
// Play: https://go.dev/play/p/_RfXJJWIsIm
|
||||
func Reduce[T any](slice []T, iteratee func(index int, item1, item2 T) T, initial T) T {
|
||||
@@ -559,24 +610,72 @@ func DeleteAt[T any](slice []T, start int, end ...int) []T {
|
||||
return slice
|
||||
}
|
||||
|
||||
// Drop creates a slice with `n` elements dropped from the beginning when n > 0, or `n` elements dropped from the ending when n < 0.
|
||||
// Play: https://go.dev/play/p/pJ-d6MUWcvK
|
||||
// Drop drop n elements from the start of a slice.
|
||||
// Play: https://go.dev/play/p/jnPO2yQsT8H
|
||||
func Drop[T any](slice []T, n int) []T {
|
||||
size := len(slice)
|
||||
|
||||
if size == 0 || n == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
if math.Abs(float64(n)) >= float64(size) {
|
||||
if size <= n {
|
||||
return []T{}
|
||||
}
|
||||
|
||||
if n < 0 {
|
||||
return slice[0 : size+n]
|
||||
if n <= 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
return slice[n:size]
|
||||
result := make([]T, 0, size-n)
|
||||
|
||||
return append(result, slice[n:]...)
|
||||
}
|
||||
|
||||
// DropRight drop n elements from the end of a slice.
|
||||
// Play: https://go.dev/play/p/8bcXvywZezG
|
||||
func DropRight[T any](slice []T, n int) []T {
|
||||
size := len(slice)
|
||||
|
||||
if size <= n {
|
||||
return []T{}
|
||||
}
|
||||
|
||||
if n <= 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
result := make([]T, 0, size-n)
|
||||
|
||||
return append(result, slice[:size-n]...)
|
||||
}
|
||||
|
||||
// DropWhile drop n elements from the start of a slice while predicate function returns true.
|
||||
// Play: https://go.dev/play/p/4rt252UV_qs
|
||||
func DropWhile[T any](slice []T, predicate func(item T) bool) []T {
|
||||
i := 0
|
||||
|
||||
for ; i < len(slice); i++ {
|
||||
if !predicate(slice[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]T, 0, len(slice)-i)
|
||||
|
||||
return append(result, slice[i:]...)
|
||||
}
|
||||
|
||||
// DropRightWhile drop n elements from the end of a slice while predicate function returns true.
|
||||
// Play: https://go.dev/play/p/6wyK3zMY56e
|
||||
func DropRightWhile[T any](slice []T, predicate func(item T) bool) []T {
|
||||
i := len(slice) - 1
|
||||
|
||||
for ; i >= 0; i-- {
|
||||
if !predicate(slice[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]T, 0, i+1)
|
||||
|
||||
return append(result, slice[:i+1]...)
|
||||
}
|
||||
|
||||
// InsertAt insert the value or other slice into slice at index.
|
||||
@@ -783,6 +882,64 @@ func Shuffle[T any](slice []T) []T {
|
||||
return slice
|
||||
}
|
||||
|
||||
// IsAscending checks if a slice is ascending order.
|
||||
// Play: https://go.dev/play/p/9CtsFjet4SH
|
||||
func IsAscending[T constraints.Ordered](slice []T) bool {
|
||||
for i := 1; i < len(slice); i++ {
|
||||
if slice[i-1] > slice[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsDescending checks if a slice is descending order.
|
||||
// Play: https://go.dev/play/p/U_LljFXma14
|
||||
func IsDescending[T constraints.Ordered](slice []T) bool {
|
||||
for i := 1; i < len(slice); i++ {
|
||||
if slice[i-1] < slice[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsSorted checks if a slice is sorted(ascending or descending).
|
||||
// Play: https://go.dev/play/p/nCE8wPLwSA-
|
||||
func IsSorted[T constraints.Ordered](slice []T) bool {
|
||||
return IsAscending(slice) || IsDescending(slice)
|
||||
}
|
||||
|
||||
// IsSortedByKey checks if a slice is sorted by iteratee function.
|
||||
// Play: https://go.dev/play/p/tUoGB7DOHI4
|
||||
func IsSortedByKey[T any, K constraints.Ordered](slice []T, iteratee func(item T) K) bool {
|
||||
size := len(slice)
|
||||
|
||||
isAscending := func(data []T) bool {
|
||||
for i := 0; i < size-1; i++ {
|
||||
if iteratee(data[i]) > iteratee(data[i+1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
isDescending := func(data []T) bool {
|
||||
for i := 0; i < size-1; i++ {
|
||||
if iteratee(data[i]) < iteratee(data[i+1]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return isAscending(slice) || isDescending(slice)
|
||||
}
|
||||
|
||||
// Sort sorts a slice of any ordered type(number or string), use quick sort algrithm.
|
||||
// default sort order is ascending (asc), if want descending order, set param `sortOrder` to `desc`.
|
||||
// Play: https://go.dev/play/p/V9AVjzf_4Fk
|
||||
@@ -937,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
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func ExampleContain() {
|
||||
@@ -18,6 +19,32 @@ func ExampleContain() {
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleContainBy() {
|
||||
type foo struct {
|
||||
A string
|
||||
B int
|
||||
}
|
||||
|
||||
array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}}
|
||||
result1 := ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 })
|
||||
result2 := ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 })
|
||||
|
||||
array2 := []string{"a", "b", "c"}
|
||||
result3 := ContainBy(array2, func(t string) bool { return t == "a" })
|
||||
result4 := ContainBy(array2, func(t string) bool { return t == "d" })
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleContainSubSlice() {
|
||||
result1 := ContainSubSlice([]string{"a", "b", "c"}, []string{"a", "b"})
|
||||
result2 := ContainSubSlice([]string{"a", "b", "c"}, []string{"a", "d"})
|
||||
@@ -352,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}
|
||||
|
||||
@@ -367,6 +413,38 @@ func ExampleMap() {
|
||||
// [2 3 4]
|
||||
}
|
||||
|
||||
func ExampleFilterMap() {
|
||||
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
|
||||
}
|
||||
|
||||
result := FilterMap(nums, getEvenNumStr)
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
|
||||
// Output:
|
||||
// []string{"2", "4"}
|
||||
}
|
||||
|
||||
func ExampleFlatMap() {
|
||||
nums := []int{1, 2, 3, 4}
|
||||
|
||||
result := FlatMap(nums, func(i int, num int) []string {
|
||||
s := "hi-" + strconv.FormatInt(int64(num), 10)
|
||||
return []string{s}
|
||||
})
|
||||
|
||||
fmt.Printf("%#v", result)
|
||||
|
||||
// Output:
|
||||
// []string{"hi-1", "hi-2", "hi-3", "hi-4"}
|
||||
}
|
||||
|
||||
func ExampleReduce() {
|
||||
nums := []int{1, 2, 3}
|
||||
|
||||
@@ -482,10 +560,74 @@ func ExampleDrop() {
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [b c]
|
||||
// [a b]
|
||||
// [a b c]
|
||||
// []
|
||||
}
|
||||
|
||||
func ExampleDropRight() {
|
||||
result1 := DropRight([]string{"a", "b", "c"}, 0)
|
||||
result2 := DropRight([]string{"a", "b", "c"}, 1)
|
||||
result3 := DropRight([]string{"a", "b", "c"}, -1)
|
||||
result4 := DropRight([]string{"a", "b", "c"}, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [a b]
|
||||
// [a b c]
|
||||
// []
|
||||
}
|
||||
|
||||
func ExampleDropWhile() {
|
||||
numbers := []int{1, 2, 3, 4, 5}
|
||||
|
||||
result1 := DropWhile(numbers, func(n int) bool {
|
||||
return n != 2
|
||||
})
|
||||
result2 := DropWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
result3 := DropWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [2 3 4 5]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
|
||||
func ExampleDropRightWhile() {
|
||||
numbers := []int{1, 2, 3, 4, 5}
|
||||
|
||||
result1 := DropRightWhile(numbers, func(n int) bool {
|
||||
return n != 2
|
||||
})
|
||||
result2 := DropRightWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
result3 := DropRightWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// []
|
||||
// [1 2 3 4 5]
|
||||
}
|
||||
|
||||
func ExampleInsertAt() {
|
||||
result1 := InsertAt([]string{"a", "b", "c"}, 0, "1")
|
||||
result2 := InsertAt([]string{"a", "b", "c"}, 1, "1")
|
||||
@@ -621,6 +763,75 @@ func ExampleReverse() {
|
||||
// [d c b a]
|
||||
}
|
||||
|
||||
func ExampleIsAscending() {
|
||||
|
||||
result1 := IsAscending([]int{1, 2, 3, 4, 5})
|
||||
result2 := IsAscending([]int{5, 4, 3, 2, 1})
|
||||
result3 := IsAscending([]int{2, 1, 3, 4, 5})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleIsDescending() {
|
||||
|
||||
result1 := IsDescending([]int{5, 4, 3, 2, 1})
|
||||
result2 := IsDescending([]int{1, 2, 3, 4, 5})
|
||||
result3 := IsDescending([]int{2, 1, 3, 4, 5})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleIsSorted() {
|
||||
|
||||
result1 := IsSorted([]int{1, 2, 3, 4, 5})
|
||||
result2 := IsSorted([]int{5, 4, 3, 2, 1})
|
||||
result3 := IsSorted([]int{2, 1, 3, 4, 5})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleIsSortedByKey() {
|
||||
result1 := IsSortedByKey([]string{"a", "ab", "abc"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
result2 := IsSortedByKey([]string{"abc", "ab", "a"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
result3 := IsSortedByKey([]string{"abc", "a", "ab"}, func(s string) int {
|
||||
return len(s)
|
||||
})
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleSort() {
|
||||
nums := []int{1, 4, 3, 2, 5}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package slice
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
@@ -19,6 +20,28 @@ func TestContain(t *testing.T) {
|
||||
assert.Equal(true, Contain([]int{1, 2, 3}, 1))
|
||||
}
|
||||
|
||||
func TestContainBy(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestContainBy")
|
||||
|
||||
type foo struct {
|
||||
A string
|
||||
B int
|
||||
}
|
||||
|
||||
array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}}
|
||||
result1 := ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 })
|
||||
result2 := ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 })
|
||||
|
||||
array2 := []string{"a", "b", "c"}
|
||||
result3 := ContainBy(array2, func(t string) bool { return t == "a" })
|
||||
result4 := ContainBy(array2, func(t string) bool { return t == "d" })
|
||||
|
||||
assert.Equal(true, result1)
|
||||
assert.Equal(false, result2)
|
||||
assert.Equal(true, result3)
|
||||
assert.Equal(false, result4)
|
||||
}
|
||||
|
||||
func TestContainSubSlice(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestContainSubSlice")
|
||||
assert.Equal(true, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "a"}))
|
||||
@@ -284,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 {
|
||||
@@ -315,6 +355,36 @@ func TestMap(t *testing.T) {
|
||||
assert.Equal(studentsOfAdd10Aage, Map(students, mapFunc))
|
||||
}
|
||||
|
||||
func TestFilterMap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestFilterMap")
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
result := FilterMap(nums, getEvenNumStr)
|
||||
|
||||
assert.Equal([]string{"2", "4"}, result)
|
||||
}
|
||||
|
||||
func TestFlatMap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestFlatMap")
|
||||
|
||||
nums := []int{1, 2, 3, 4}
|
||||
|
||||
result := FlatMap(nums, func(i int, num int) []string {
|
||||
s := "hi-" + strconv.FormatInt(int64(num), 10)
|
||||
return []string{s}
|
||||
})
|
||||
|
||||
assert.Equal([]string{"hi-1", "hi-2", "hi-3", "hi-4"}, result)
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
cases := [][]int{
|
||||
{},
|
||||
@@ -377,7 +447,7 @@ func TestDeleteAt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDrop(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestInterfaceSlice")
|
||||
assert := internal.NewAssert(t, "TestDrop")
|
||||
|
||||
assert.Equal([]int{}, Drop([]int{}, 0))
|
||||
assert.Equal([]int{}, Drop([]int{}, 1))
|
||||
@@ -388,9 +458,64 @@ func TestDrop(t *testing.T) {
|
||||
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 5))
|
||||
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 6))
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4}, Drop([]int{1, 2, 3, 4, 5}, -1))
|
||||
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, -6))
|
||||
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, -6))
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, -1))
|
||||
}
|
||||
|
||||
func TestDropRight(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDropRight")
|
||||
|
||||
assert.Equal([]int{}, DropRight([]int{}, 0))
|
||||
assert.Equal([]int{}, DropRight([]int{}, 1))
|
||||
assert.Equal([]int{}, DropRight([]int{}, -1))
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, 0))
|
||||
assert.Equal([]int{1, 2, 3, 4}, DropRight([]int{1, 2, 3, 4, 5}, 1))
|
||||
assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 5))
|
||||
assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 6))
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, -1))
|
||||
}
|
||||
|
||||
func TestDropWhile(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDropWhile")
|
||||
|
||||
numbers := []int{1, 2, 3, 4, 5}
|
||||
|
||||
r1 := DropWhile(numbers, func(n int) bool {
|
||||
return n != 2
|
||||
})
|
||||
assert.Equal([]int{2, 3, 4, 5}, r1)
|
||||
|
||||
r2 := DropWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
assert.Equal([]int{}, r2)
|
||||
|
||||
r3 := DropWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, r3)
|
||||
}
|
||||
|
||||
func TestDropRightWhile(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDropRightWhile")
|
||||
|
||||
numbers := []int{1, 2, 3, 4, 5}
|
||||
|
||||
r1 := DropRightWhile(numbers, func(n int) bool {
|
||||
return n != 2
|
||||
})
|
||||
assert.Equal([]int{1, 2}, r1)
|
||||
|
||||
r2 := DropRightWhile(numbers, func(n int) bool {
|
||||
return true
|
||||
})
|
||||
assert.Equal([]int{}, r2)
|
||||
|
||||
r3 := DropRightWhile(numbers, func(n int) bool {
|
||||
return n == 0
|
||||
})
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, r3)
|
||||
}
|
||||
|
||||
func TestInsertAt(t *testing.T) {
|
||||
@@ -540,14 +665,54 @@ 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
|
||||
}
|
||||
assert.Equal([]int{1, 2}, DifferenceBy(s1, s2, addOne))
|
||||
}
|
||||
|
||||
func TestIsAscending(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsAscending")
|
||||
|
||||
assert.Equal(true, IsAscending([]int{1, 2, 3, 4, 5}))
|
||||
assert.Equal(false, IsAscending([]int{5, 4, 3, 2, 1}))
|
||||
assert.Equal(false, IsAscending([]int{2, 1, 3, 4, 5}))
|
||||
}
|
||||
|
||||
func TestIsDescending(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsDescending")
|
||||
|
||||
assert.Equal(true, IsDescending([]int{5, 4, 3, 2, 1}))
|
||||
assert.Equal(false, IsDescending([]int{1, 2, 3, 4, 5}))
|
||||
assert.Equal(false, IsDescending([]int{2, 1, 3, 4, 5}))
|
||||
}
|
||||
|
||||
func TestIsSorted(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsSorted")
|
||||
|
||||
assert.Equal(true, IsSorted([]int{5, 4, 3, 2, 1}))
|
||||
assert.Equal(true, IsSorted([]int{1, 2, 3, 4, 5}))
|
||||
assert.Equal(false, IsSorted([]int{2, 1, 3, 4, 5}))
|
||||
}
|
||||
|
||||
func TestIsSortedByKey(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIsSortedByKey")
|
||||
|
||||
assert.Equal(true, IsSortedByKey([]string{"a", "ab", "abc"}, func(s string) int {
|
||||
return len(s)
|
||||
}))
|
||||
|
||||
assert.Equal(true, IsSortedByKey([]string{"abc", "ab", "a"}, func(s string) int {
|
||||
return len(s)
|
||||
}))
|
||||
|
||||
assert.Equal(false, IsSortedByKey([]string{"abc", "a", "ab"}, func(s string) int {
|
||||
return len(s)
|
||||
}))
|
||||
}
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSort")
|
||||
|
||||
@@ -708,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"))
|
||||
}
|
||||
|
||||
|
||||
118
stream/stream.go
118
stream/stream.go
@@ -10,6 +10,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
@@ -23,7 +24,7 @@ import (
|
||||
// Map(mapper func(item T) T) StreamI[T]
|
||||
// Peek(consumer func(item T)) StreamI[T]
|
||||
|
||||
// Sort(less func(a, b T) bool) StreamI[T]
|
||||
// Sorted(less func(a, b T) bool) StreamI[T]
|
||||
// Max(less func(a, b T) bool) (T, bool)
|
||||
// Min(less func(a, b T) bool) (T, bool)
|
||||
|
||||
@@ -43,7 +44,7 @@ import (
|
||||
|
||||
// // part of methods custom extension
|
||||
// Reverse() StreamI[T]
|
||||
// Range(start, end int64) StreamI[T]
|
||||
// Range(start, end int) StreamI[T]
|
||||
// Concat(streams ...StreamI[T]) StreamI[T]
|
||||
// }
|
||||
|
||||
@@ -63,6 +64,7 @@ func Generate[T any](generator func() func() (item T, ok bool)) stream[T] {
|
||||
|
||||
var zeroValue T
|
||||
for next, item, ok := generator(), zeroValue, true; ok; {
|
||||
|
||||
item, ok = next()
|
||||
if ok {
|
||||
source = append(source, item)
|
||||
@@ -72,12 +74,12 @@ func Generate[T any](generator func() func() (item T, ok bool)) stream[T] {
|
||||
return FromSlice(source)
|
||||
}
|
||||
|
||||
// FromSlice create stream from slice.
|
||||
// FromSlice creates stream from slice.
|
||||
func FromSlice[T any](source []T) stream[T] {
|
||||
return stream[T]{source: source}
|
||||
}
|
||||
|
||||
// FromChannel create stream from channel.
|
||||
// FromChannel creates stream from channel.
|
||||
func FromChannel[T any](source <-chan T) stream[T] {
|
||||
s := make([]T, 0)
|
||||
|
||||
@@ -88,7 +90,7 @@ func FromChannel[T any](source <-chan T) stream[T] {
|
||||
return FromSlice(s)
|
||||
}
|
||||
|
||||
// FromRange create a number stream from start to end. both start and end are included. [start, end]
|
||||
// FromRange creates a number stream from start to end. both start and end are included. [start, end]
|
||||
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) stream[T] {
|
||||
if end < start {
|
||||
panic("stream.FromRange: param start should be before param end")
|
||||
@@ -97,7 +99,7 @@ func FromRange[T constraints.Integer | constraints.Float](start, end, step T) st
|
||||
}
|
||||
|
||||
l := int((end-start)/step) + 1
|
||||
source := make([]T, l, l)
|
||||
source := make([]T, l)
|
||||
|
||||
for i := 0; i < l; i++ {
|
||||
source[i] = start + (T(i) * step)
|
||||
@@ -106,6 +108,16 @@ func FromRange[T constraints.Integer | constraints.Float](start, end, step T) st
|
||||
return FromSlice(source)
|
||||
}
|
||||
|
||||
// Concat creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream.
|
||||
func Concat[T any](a, b stream[T]) stream[T] {
|
||||
source := make([]T, 0)
|
||||
|
||||
source = append(source, a.source...)
|
||||
source = append(source, b.source...)
|
||||
|
||||
return FromSlice(source)
|
||||
}
|
||||
|
||||
// Distinct returns a stream that removes the duplicated items.
|
||||
func (s stream[T]) Distinct() stream[T] {
|
||||
source := make([]T, 0)
|
||||
@@ -149,7 +161,7 @@ func (s stream[T]) Filter(predicate func(item T) bool) stream[T] {
|
||||
|
||||
// Map returns a stream consisting of the elements of this stream that apply the given function to elements of stream.
|
||||
func (s stream[T]) Map(mapper func(item T) T) stream[T] {
|
||||
source := make([]T, s.Count(), s.Count())
|
||||
source := make([]T, s.Count())
|
||||
|
||||
for i, v := range s.source {
|
||||
source[i] = mapper(v)
|
||||
@@ -255,6 +267,98 @@ func (s stream[T]) Count() int {
|
||||
return len(s.source)
|
||||
}
|
||||
|
||||
// FindFirst returns the first element of this stream and true, or zero value and false if the stream is empty.
|
||||
func (s stream[T]) FindFirst() (T, bool) {
|
||||
var result T
|
||||
|
||||
if s.source == nil || len(s.source) == 0 {
|
||||
return result, false
|
||||
}
|
||||
|
||||
return s.source[0], true
|
||||
}
|
||||
|
||||
// Reverse returns a stream whose elements are reverse order of given stream.
|
||||
func (s stream[T]) Reverse() stream[T] {
|
||||
l := len(s.source)
|
||||
source := make([]T, l)
|
||||
|
||||
for i := 0; i < l; i++ {
|
||||
source[i] = s.source[l-1-i]
|
||||
}
|
||||
return FromSlice(source)
|
||||
}
|
||||
|
||||
// Range returns a stream whose elements are in the range from start(included) to end(excluded) original stream.
|
||||
func (s stream[T]) Range(start, end int) stream[T] {
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end < 0 {
|
||||
end = 0
|
||||
}
|
||||
if start >= end {
|
||||
return FromSlice([]T{})
|
||||
}
|
||||
|
||||
source := make([]T, 0)
|
||||
|
||||
if end > len(s.source) {
|
||||
end = len(s.source)
|
||||
}
|
||||
|
||||
for i := start; i < end; i++ {
|
||||
source = append(source, s.source[i])
|
||||
}
|
||||
|
||||
return FromSlice(source)
|
||||
}
|
||||
|
||||
// Sorted returns a stream consisting of the elements of this stream, sorted according to the provided less function.
|
||||
func (s stream[T]) Sorted(less func(a, b T) bool) stream[T] {
|
||||
source := []T{}
|
||||
source = append(source, s.source...)
|
||||
|
||||
slice.SortBy(source, less)
|
||||
|
||||
return FromSlice(source)
|
||||
}
|
||||
|
||||
// Max returns the maximum element of this stream according to the provided less function.
|
||||
// less: a > b
|
||||
func (s stream[T]) Max(less func(a, b T) bool) (T, bool) {
|
||||
var max T
|
||||
|
||||
if len(s.source) == 0 {
|
||||
return max, false
|
||||
}
|
||||
|
||||
for i, v := range s.source {
|
||||
if less(v, max) || i == 0 {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
return max, true
|
||||
}
|
||||
|
||||
// Min returns the minimum element of this stream according to the provided less function.
|
||||
// less: a < b
|
||||
func (s stream[T]) Min(less func(a, b T) bool) (T, bool) {
|
||||
var min T
|
||||
|
||||
if len(s.source) == 0 {
|
||||
return min, false
|
||||
}
|
||||
|
||||
for i, v := range s.source {
|
||||
if less(v, min) || i == 0 {
|
||||
min = v
|
||||
}
|
||||
}
|
||||
|
||||
return min, true
|
||||
}
|
||||
|
||||
// ToSlice return the elements in the stream.
|
||||
func (s stream[T]) ToSlice() []T {
|
||||
return s.source
|
||||
|
||||
@@ -247,3 +247,92 @@ func TestStream_Reduce(t *testing.T) {
|
||||
|
||||
assert.Equal(6, result)
|
||||
}
|
||||
|
||||
func TestStream_FindFirst(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStream_FindFirst")
|
||||
|
||||
stream := FromSlice([]int{1, 2, 3})
|
||||
|
||||
result, ok := stream.FindFirst()
|
||||
|
||||
assert.Equal(1, result)
|
||||
assert.Equal(true, ok)
|
||||
}
|
||||
|
||||
func TestStream_Reverse(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStream_Reverse")
|
||||
|
||||
s := FromSlice([]int{1, 2, 3})
|
||||
|
||||
rs := s.Reverse()
|
||||
|
||||
assert.Equal([]int{3, 2, 1}, rs.ToSlice())
|
||||
}
|
||||
|
||||
func TestStream_Range(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStream_Range")
|
||||
|
||||
s := FromSlice([]int{1, 2, 3})
|
||||
|
||||
s1 := s.Range(-1, 0)
|
||||
assert.Equal([]int{}, s1.ToSlice())
|
||||
|
||||
s2 := s.Range(0, -1)
|
||||
assert.Equal([]int{}, s2.ToSlice())
|
||||
|
||||
s3 := s.Range(0, 0)
|
||||
assert.Equal([]int{}, s3.ToSlice())
|
||||
|
||||
s4 := s.Range(1, 1)
|
||||
assert.Equal([]int{}, s4.ToSlice())
|
||||
|
||||
s5 := s.Range(0, 1)
|
||||
assert.Equal([]int{1}, s5.ToSlice())
|
||||
|
||||
s6 := s.Range(0, 4)
|
||||
assert.Equal([]int{1, 2, 3}, s6.ToSlice())
|
||||
}
|
||||
|
||||
func TestStream_Concat(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStream_Concat")
|
||||
|
||||
s1 := FromSlice([]int{1, 2, 3})
|
||||
s2 := FromSlice([]int{4, 5, 6})
|
||||
|
||||
s := Concat(s1, s2)
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4, 5, 6}, s.ToSlice())
|
||||
}
|
||||
|
||||
func TestStream_Sorted(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStream_Sorted")
|
||||
|
||||
s := FromSlice([]int{4, 2, 1, 3})
|
||||
|
||||
s1 := s.Sorted(func(a, b int) bool { return a < b })
|
||||
|
||||
assert.Equal([]int{4, 2, 1, 3}, s.ToSlice())
|
||||
assert.Equal([]int{1, 2, 3, 4}, s1.ToSlice())
|
||||
}
|
||||
|
||||
func TestStream_Max(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStream_Max")
|
||||
|
||||
s := FromSlice([]int{4, 2, 1, 3})
|
||||
|
||||
max, ok := s.Max(func(a, b int) bool { return a > b })
|
||||
|
||||
assert.Equal(4, max)
|
||||
assert.Equal(true, ok)
|
||||
}
|
||||
|
||||
func TestStream_Min(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestStream_Min")
|
||||
|
||||
s := FromSlice([]int{4, 2, 1, 3})
|
||||
|
||||
max, ok := s.Max(func(a, b int) bool { return a < b })
|
||||
|
||||
assert.Equal(1, max)
|
||||
assert.Equal(true, ok)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -67,44 +67,25 @@ func LowerFirst(s string) string {
|
||||
return string(r) + s[size:]
|
||||
}
|
||||
|
||||
// PadEnd pads string on the right side if it's shorter than size.
|
||||
// PadStart pads string on the left and right side if it's shorter than size.
|
||||
// Padding characters are truncated if they exceed size.
|
||||
// Play: https://go.dev/play/p/9xP8rN0vz--
|
||||
func PadEnd(source string, size int, padStr string) string {
|
||||
len1 := len(source)
|
||||
len2 := len(padStr)
|
||||
|
||||
if len1 >= size {
|
||||
return source
|
||||
}
|
||||
|
||||
fill := ""
|
||||
if len2 >= size-len1 {
|
||||
fill = padStr[0 : size-len1]
|
||||
} else {
|
||||
fill = strings.Repeat(padStr, size-len1)
|
||||
}
|
||||
return source + fill[0:size-len1]
|
||||
// Play: https://go.dev/play/p/NzImQq-VF8q
|
||||
func Pad(source string, size int, padStr string) string {
|
||||
return padAtPosition(source, size, padStr, 0)
|
||||
}
|
||||
|
||||
// PadStart pads string on the left side if it's shorter than size.
|
||||
// Padding characters are truncated if they exceed size.
|
||||
// Play: https://go.dev/play/p/xpTfzArDfvT
|
||||
func PadStart(source string, size int, padStr string) string {
|
||||
len1 := len(source)
|
||||
len2 := len(padStr)
|
||||
return padAtPosition(source, size, padStr, 1)
|
||||
}
|
||||
|
||||
if len1 >= size {
|
||||
return source
|
||||
}
|
||||
|
||||
fill := ""
|
||||
if len2 >= size-len1 {
|
||||
fill = padStr[0 : size-len1]
|
||||
} else {
|
||||
fill = strings.Repeat(padStr, size-len1)
|
||||
}
|
||||
return fill[0:size-len1] + source
|
||||
// PadEnd pads string on the right side if it's shorter than size.
|
||||
// Padding characters are truncated if they exceed size.
|
||||
// Play: https://go.dev/play/p/9xP8rN0vz--
|
||||
func PadEnd(source string, size int, padStr string) string {
|
||||
return padAtPosition(source, size, padStr, 2)
|
||||
}
|
||||
|
||||
// KebabCase coverts string to kebab-case, non letters and numbers will be ignored.
|
||||
@@ -306,3 +287,89 @@ func Substring(s string, offset int, length uint) string {
|
||||
|
||||
return strings.Replace(str, "\x00", "", -1)
|
||||
}
|
||||
|
||||
// SplitWords splits a string into words, word only contains alphabetic characters.
|
||||
// Play: https://go.dev/play/p/KLiX4WiysMM
|
||||
func SplitWords(s string) []string {
|
||||
var word string
|
||||
var words []string
|
||||
var r rune
|
||||
var size, pos int
|
||||
|
||||
isWord := false
|
||||
|
||||
for len(s) > 0 {
|
||||
r, size = utf8.DecodeRuneInString(s)
|
||||
|
||||
switch {
|
||||
case isLetter(r):
|
||||
if !isWord {
|
||||
isWord = true
|
||||
word = s
|
||||
pos = 0
|
||||
}
|
||||
|
||||
case isWord && (r == '\'' || r == '-'):
|
||||
// is word
|
||||
|
||||
default:
|
||||
if isWord {
|
||||
isWord = false
|
||||
words = append(words, word[:pos])
|
||||
}
|
||||
}
|
||||
|
||||
pos += size
|
||||
s = s[size:]
|
||||
}
|
||||
|
||||
if isWord {
|
||||
words = append(words, word[:pos])
|
||||
}
|
||||
|
||||
return words
|
||||
}
|
||||
|
||||
// WordCount return the number of meaningful word, word only contains alphabetic characters.
|
||||
// Play: https://go.dev/play/p/bj7_odx3vRf
|
||||
func WordCount(s string) int {
|
||||
var r rune
|
||||
var size, count int
|
||||
|
||||
isWord := false
|
||||
|
||||
for len(s) > 0 {
|
||||
r, size = utf8.DecodeRuneInString(s)
|
||||
|
||||
switch {
|
||||
case isLetter(r):
|
||||
if !isWord {
|
||||
isWord = true
|
||||
count++
|
||||
}
|
||||
|
||||
case isWord && (r == '\'' || r == '-'):
|
||||
// is word
|
||||
|
||||
default:
|
||||
isWord = false
|
||||
}
|
||||
|
||||
s = s[size:]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -186,6 +186,33 @@ func ExampleUpperFirst() {
|
||||
// Bar大
|
||||
}
|
||||
|
||||
func ExamplePad() {
|
||||
result1 := Pad("foo", 1, "bar")
|
||||
result2 := Pad("foo", 2, "bar")
|
||||
result3 := Pad("foo", 3, "bar")
|
||||
result4 := Pad("foo", 4, "bar")
|
||||
result5 := Pad("foo", 5, "bar")
|
||||
result6 := Pad("foo", 6, "bar")
|
||||
result7 := Pad("foo", 7, "bar")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
fmt.Println(result7)
|
||||
|
||||
// Output:
|
||||
// foo
|
||||
// foo
|
||||
// foo
|
||||
// foob
|
||||
// bfoob
|
||||
// bfooba
|
||||
// bafooba
|
||||
}
|
||||
|
||||
func ExamplePadEnd() {
|
||||
result1 := PadEnd("foo", 1, "bar")
|
||||
result2 := PadEnd("foo", 2, "bar")
|
||||
@@ -361,3 +388,64 @@ func ExampleSubstring() {
|
||||
// de
|
||||
// 你好
|
||||
}
|
||||
|
||||
func ExampleSplitWords() {
|
||||
|
||||
result1 := SplitWords("a word")
|
||||
result2 := SplitWords("I'am a programmer")
|
||||
result3 := SplitWords("Bonjour, je suis programmeur")
|
||||
result4 := SplitWords("a -b-c' 'd'e")
|
||||
result5 := SplitWords("你好,我是一名码农")
|
||||
result6 := SplitWords("こんにちは,私はプログラマーです")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// [a word]
|
||||
// [I'am a programmer]
|
||||
// [Bonjour je suis programmeur]
|
||||
// [a b-c' d'e]
|
||||
// []
|
||||
// []
|
||||
}
|
||||
|
||||
func ExampleWordCount() {
|
||||
|
||||
result1 := WordCount("a word")
|
||||
result2 := WordCount("I'am a programmer")
|
||||
result3 := WordCount("Bonjour, je suis programmeur")
|
||||
result4 := WordCount("a -b-c' 'd'e")
|
||||
result5 := WordCount("你好,我是一名码农")
|
||||
result6 := WordCount("こんにちは,私はプログラマーです")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
fmt.Println(result6)
|
||||
|
||||
// Output:
|
||||
// 2
|
||||
// 3
|
||||
// 4
|
||||
// 3
|
||||
// 0
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleRemoveNonPrintable() {
|
||||
result1 := RemoveNonPrintable("hello\u00a0 \u200bworld\n")
|
||||
result2 := RemoveNonPrintable("你好😄")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
// Output:
|
||||
// hello world
|
||||
// 你好😄
|
||||
}
|
||||
|
||||
@@ -98,3 +98,73 @@ func toUpperAll(rs []rune) []rune {
|
||||
}
|
||||
return rs
|
||||
}
|
||||
|
||||
// padWithPosition pads string
|
||||
func padAtPosition(str string, length int, padStr string, position int) string {
|
||||
if len(str) >= length {
|
||||
return str
|
||||
}
|
||||
|
||||
if padStr == "" {
|
||||
padStr = " "
|
||||
}
|
||||
|
||||
length = length - len(str)
|
||||
startPadLen := 0
|
||||
if position == 0 {
|
||||
startPadLen = length / 2
|
||||
} else if position == 1 {
|
||||
startPadLen = length
|
||||
}
|
||||
endPadLen := length - startPadLen
|
||||
|
||||
charLen := len(padStr)
|
||||
leftPad := ""
|
||||
cur := 0
|
||||
for cur < startPadLen {
|
||||
leftPad += string(padStr[cur%charLen])
|
||||
cur++
|
||||
}
|
||||
|
||||
cur = 0
|
||||
rightPad := ""
|
||||
for cur < endPadLen {
|
||||
rightPad += string(padStr[cur%charLen])
|
||||
cur++
|
||||
}
|
||||
|
||||
return leftPad + str + rightPad
|
||||
}
|
||||
|
||||
// isLetter checks r is a letter but not CJK character.
|
||||
func isLetter(r rune) bool {
|
||||
if !unicode.IsLetter(r) {
|
||||
return false
|
||||
}
|
||||
|
||||
switch {
|
||||
// cjk char: /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]/
|
||||
|
||||
// hiragana and katakana (Japanese only)
|
||||
case r >= '\u3034' && r < '\u30ff':
|
||||
return false
|
||||
|
||||
// CJK unified ideographs extension A (Chinese, Japanese, and Korean)
|
||||
case r >= '\u3400' && r < '\u4dbf':
|
||||
return false
|
||||
|
||||
// CJK unified ideographs (Chinese, Japanese, and Korean)
|
||||
case r >= '\u4e00' && r < '\u9fff':
|
||||
return false
|
||||
|
||||
// CJK compatibility ideographs (Chinese, Japanese, and Korean)
|
||||
case r >= '\uf900' && r < '\ufaff':
|
||||
return false
|
||||
|
||||
// half-width katakana (Japanese only)
|
||||
case r >= '\uff66' && r < '\uff9f':
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -168,9 +168,19 @@ func TestLowerFirst(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPad(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPad")
|
||||
|
||||
assert.Equal("a ", Pad("a", 2, ""))
|
||||
assert.Equal("a", Pad("a", 1, "b"))
|
||||
assert.Equal("ab", Pad("a", 2, "b"))
|
||||
assert.Equal("mabcdm", Pad("abcd", 6, "m"))
|
||||
}
|
||||
|
||||
func TestPadEnd(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestPadEnd")
|
||||
|
||||
assert.Equal("a ", PadEnd("a", 2, " "))
|
||||
assert.Equal("a", PadEnd("a", 1, "b"))
|
||||
assert.Equal("ab", PadEnd("a", 2, "b"))
|
||||
assert.Equal("abcdmn", PadEnd("abcd", 6, "mno"))
|
||||
@@ -298,3 +308,44 @@ func TestSubstring(t *testing.T) {
|
||||
assert.Equal("de", Substring("abcde", -2, 3))
|
||||
assert.Equal("你好", Substring("你好,欢迎你", 0, 2))
|
||||
}
|
||||
|
||||
func TestSplitWords(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSplitWords")
|
||||
|
||||
cases := map[string][]string{
|
||||
"a word": {"a", "word"},
|
||||
"I'am a programmer": {"I'am", "a", "programmer"},
|
||||
"Bonjour, je suis programmeur": {"Bonjour", "je", "suis", "programmeur"},
|
||||
"a -b-c' 'd'e": {"a", "b-c'", "d'e"},
|
||||
"你好,我是一名码农": nil,
|
||||
"こんにちは,私はプログラマーです": nil,
|
||||
}
|
||||
|
||||
for k, v := range cases {
|
||||
assert.Equal(v, SplitWords(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWordCount(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestSplitWords")
|
||||
|
||||
cases := map[string]int{
|
||||
"a word": 2, // {"a", "word"},
|
||||
"I'am a programmer": 3, // {"I'am", "a", "programmer"},
|
||||
"Bonjour, je suis programmeur": 4, // {"Bonjour", "je", "suis", "programmeur"},
|
||||
"a -b-c' 'd'e": 3, // {"a", "b-c'", "d'e"},
|
||||
"你好,我是一名码农": 0, // nil,
|
||||
"こんにちは,私はプログラマーです": 0, // nil,
|
||||
}
|
||||
|
||||
for k, v := range cases {
|
||||
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 {
|
||||
@@ -281,6 +306,10 @@ func IsZeroValue(value any) bool {
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(value)
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
|
||||
if !rv.IsValid() {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
228
xerror/stack.go
Normal file
228
xerror/stack.go
Normal file
@@ -0,0 +1,228 @@
|
||||
// Copyright 2023 dudaodong@gmail.com. All rights reserved.
|
||||
// Use of this source code is governed by MIT license
|
||||
|
||||
package xerror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Stack contains function, file and line number info in the stack trace.
|
||||
type Stack struct {
|
||||
Func string `json:"func"`
|
||||
File string `json:"file"`
|
||||
Line int `json:"line"`
|
||||
}
|
||||
|
||||
// Stacks returns stack trace array generated by pkg/errors
|
||||
func (e *XError) Stacks() []*Stack {
|
||||
resp := make([]*Stack, len(*e.stack))
|
||||
for i, st := range *e.stack {
|
||||
f := frame(st)
|
||||
resp[i] = &Stack{
|
||||
Func: f.name(),
|
||||
File: f.file(),
|
||||
Line: f.line(),
|
||||
}
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
// StackTrace returns stack trace which is compatible with pkg/errors
|
||||
func (e *XError) StackTrace() StackTrace {
|
||||
return e.stack.StackTrace()
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// Stacktrace part is implemented based on copy of https://github.com/pkg/errors
|
||||
//
|
||||
// Copyright (c) 2015, Dave Cheney <dave@cheney.net>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
type frame uintptr
|
||||
type stack []uintptr
|
||||
|
||||
// StackTrace is array of frame. It's exported for compatibility with github.com/pkg/errors
|
||||
type StackTrace []frame
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
// multiple frames may have the same PC value.
|
||||
func (f frame) pc() uintptr { return uintptr(f) - 1 }
|
||||
|
||||
// file returns the full path to the file that contains the
|
||||
// function for this Frame's pc.
|
||||
func (f frame) file() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
file, _ := fn.FileLine(f.pc())
|
||||
return file
|
||||
}
|
||||
|
||||
// line returns the line number of source code of the
|
||||
// function for this Frame's pc.
|
||||
func (f frame) line() int {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return 0
|
||||
}
|
||||
_, line := fn.FileLine(f.pc())
|
||||
return line
|
||||
}
|
||||
|
||||
// name returns the name of this function, if known.
|
||||
func (f frame) name() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
return fn.Name()
|
||||
}
|
||||
|
||||
// Format of frame formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
// %d source line
|
||||
// %n function name
|
||||
// %v equivalent to %s:%d
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s function name and path of source file relative to the compile time
|
||||
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
_, _ = io.WriteString(s, f.name())
|
||||
_, _ = io.WriteString(s, "\n\t")
|
||||
_, _ = io.WriteString(s, f.file())
|
||||
default:
|
||||
_, _ = io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
_, _ = io.WriteString(s, strconv.Itoa(f.line()))
|
||||
case 'n':
|
||||
_, _ = io.WriteString(s, funcname(f.name()))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
_, _ = io.WriteString(s, ":")
|
||||
f.Format(s, 'd')
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalText formats a stacktrace Frame as a text string. The output is the
|
||||
// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
|
||||
func (f frame) MarshalText() ([]byte, error) {
|
||||
name := f.name()
|
||||
if name == "unknown" {
|
||||
return []byte(name), nil
|
||||
}
|
||||
return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
|
||||
}
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
for _, f := range st {
|
||||
_, _ = io.WriteString(s, "\n")
|
||||
f.Format(s, verb)
|
||||
}
|
||||
case s.Flag('#'):
|
||||
fmt.Fprintf(s, "%#v", []frame(st))
|
||||
default:
|
||||
st.formatSlice(s, verb)
|
||||
}
|
||||
case 's':
|
||||
st.formatSlice(s, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// formatSlice will format this StackTrace into the given buffer as a slice of
|
||||
// Frame, only valid when called with '%s' or '%v'.
|
||||
func (st StackTrace) formatSlice(s fmt.State, verb rune) {
|
||||
_, _ = io.WriteString(s, "[")
|
||||
for i, f := range st {
|
||||
if i > 0 {
|
||||
_, _ = io.WriteString(s, " ")
|
||||
}
|
||||
f.Format(s, verb)
|
||||
}
|
||||
_, _ = io.WriteString(s, "]")
|
||||
}
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := frame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) StackTrace() StackTrace {
|
||||
frames := make([]frame, len(*s))
|
||||
for i := 0; i < len(frames); i++ {
|
||||
frames[i] = frame((*s)[i])
|
||||
}
|
||||
return frames
|
||||
}
|
||||
|
||||
func callers() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(4, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// funcname removes the path prefix component of a function's name reported by func.Name().
|
||||
func funcname(name string) string {
|
||||
i := strings.LastIndex(name, "/")
|
||||
name = name[i+1:]
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
196
xerror/xerror.go
196
xerror/xerror.go
@@ -4,10 +4,202 @@
|
||||
// Package xerror implements helpers for errors
|
||||
package xerror
|
||||
|
||||
// Unwrap if err is nil then it returns a valid value
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/duke-git/lancet/v2/random"
|
||||
)
|
||||
|
||||
// XError is to handle error related information.
|
||||
type XError struct {
|
||||
id string
|
||||
message string
|
||||
stack *stack
|
||||
cause error
|
||||
values map[string]any
|
||||
}
|
||||
|
||||
// New creates a new XError with message
|
||||
func New(format string, args ...any) *XError {
|
||||
err := newXError()
|
||||
err.message = fmt.Sprintf(format, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
// Wrap creates a new XError and add message.
|
||||
func Wrap(cause error, message ...any) *XError {
|
||||
err := newXError()
|
||||
|
||||
if len(message) > 0 {
|
||||
var newMsgs []string
|
||||
for _, m := range message {
|
||||
newMsgs = append(newMsgs, fmt.Sprintf("%v", m))
|
||||
}
|
||||
err.message = strings.Join(newMsgs, " ")
|
||||
}
|
||||
|
||||
err.cause = cause
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Unwrap returns unwrapped XError from err by errors.As. If no XError, returns nil
|
||||
func Unwrap(err error) *XError {
|
||||
var e *XError
|
||||
if errors.As(err, &e) {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newXError() *XError {
|
||||
id, err := random.UUIdV4()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &XError{
|
||||
id: id,
|
||||
stack: callers(),
|
||||
values: make(map[string]any),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *XError) copy(dest *XError) {
|
||||
dest.message = e.message
|
||||
dest.id = e.id
|
||||
dest.cause = e.cause
|
||||
|
||||
for k, v := range e.values {
|
||||
dest.values[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Error implements standard error interface.
|
||||
func (e *XError) Error() string {
|
||||
msg := e.message
|
||||
cause := e.cause
|
||||
|
||||
if cause == nil {
|
||||
return msg
|
||||
}
|
||||
|
||||
msg = fmt.Sprintf("%s: %v", msg, cause.Error())
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
// Format returns:
|
||||
// - %v, %s, %q: formatted message
|
||||
// - %+v: formatted message with stack trace
|
||||
func (e *XError) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
_, _ = io.WriteString(s, e.Error())
|
||||
e.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
_, _ = io.WriteString(s, e.Error())
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap creates a new XError and copy message and id to new one.
|
||||
func (e *XError) Wrap(cause error) *XError {
|
||||
err := newXError()
|
||||
e.copy(err)
|
||||
err.cause = cause
|
||||
return err
|
||||
}
|
||||
|
||||
// Unwrap compatible with github.com/pkg/errors
|
||||
func (e *XError) Unwrap() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
// With adds key and value related to the error object
|
||||
func (e *XError) With(key string, value any) *XError {
|
||||
e.values[key] = value
|
||||
return e
|
||||
}
|
||||
|
||||
// Is checks if target error is XError and Error.id of two errors are matched.
|
||||
func (e *XError) Is(target error) bool {
|
||||
var err *XError
|
||||
|
||||
if errors.As(target, &err) {
|
||||
if e.id != "" && e.id == err.id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return e == target
|
||||
}
|
||||
|
||||
// Id sets id to check equality in XError.Is
|
||||
func (e *XError) Id(id string) *XError {
|
||||
e.id = id
|
||||
return e
|
||||
}
|
||||
|
||||
// Values returns map of key and value that is set by With. All wrapped xerror.XError key and values will be merged.
|
||||
// Key and values of wrapped error is overwritten by upper xerror.XError.
|
||||
func (e *XError) Values() map[string]any {
|
||||
var values map[string]any
|
||||
|
||||
if cause := e.Unwrap(); cause != nil {
|
||||
if err, ok := cause.(*XError); ok {
|
||||
values = err.Values()
|
||||
}
|
||||
}
|
||||
|
||||
if values == nil {
|
||||
values = make(map[string]any)
|
||||
}
|
||||
|
||||
for key, value := range e.values {
|
||||
values[key] = value
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
type errInfo struct {
|
||||
Message string `json:"message"`
|
||||
Id string `json:"id"`
|
||||
StackTrace []*Stack `json:"stacktrace"`
|
||||
Cause error `json:"cause"`
|
||||
Values map[string]any `json:"values"`
|
||||
}
|
||||
|
||||
// Info returns information of xerror, which can be printed.
|
||||
func (e *XError) Info() *errInfo {
|
||||
errInfo := &errInfo{
|
||||
Message: e.message,
|
||||
Id: e.id,
|
||||
StackTrace: e.Stacks(),
|
||||
Cause: e.cause,
|
||||
Values: make(map[string]any),
|
||||
}
|
||||
|
||||
for k, v := range e.values {
|
||||
errInfo.Values[k] = v
|
||||
}
|
||||
|
||||
return errInfo
|
||||
}
|
||||
|
||||
// TryUnwrap if err is nil then it returns a valid value
|
||||
// If err is not nil, Unwrap panics with err.
|
||||
// Play: https://go.dev/play/p/w84d7Mb3Afk
|
||||
func Unwrap[T any](val T, err error) T {
|
||||
func TryUnwrap[T any](val T, err error) T {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,144 @@
|
||||
package xerror
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ExampleUnwrap() {
|
||||
result1 := Unwrap(strconv.Atoi("42"))
|
||||
func ExampleNew() {
|
||||
err := New("error")
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
// error
|
||||
}
|
||||
|
||||
func ExampleWrap() {
|
||||
err := New("wrong password")
|
||||
wrapErr := Wrap(err, "error")
|
||||
|
||||
fmt.Println(wrapErr.Error())
|
||||
|
||||
// Output:
|
||||
// error: wrong password
|
||||
}
|
||||
|
||||
func ExampleXError_Wrap() {
|
||||
err1 := New("error").With("level", "high")
|
||||
err2 := err1.Wrap(errors.New("invalid username"))
|
||||
|
||||
fmt.Println(err2.Error())
|
||||
|
||||
// Output:
|
||||
// error: invalid username
|
||||
}
|
||||
|
||||
func ExampleXError_Unwrap() {
|
||||
err1 := New("error").With("level", "high")
|
||||
err2 := err1.Wrap(errors.New("invalid username"))
|
||||
|
||||
err := err2.Unwrap()
|
||||
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
// invalid username
|
||||
}
|
||||
|
||||
func ExampleXError_StackTrace() {
|
||||
err := New("error")
|
||||
|
||||
stacks := err.Stacks()
|
||||
|
||||
fmt.Println(stacks[0].Func)
|
||||
fmt.Println(stacks[0].Line)
|
||||
|
||||
containFile := strings.Contains(stacks[0].File, "xerror_example_test.go")
|
||||
fmt.Println(containFile)
|
||||
|
||||
// Output:
|
||||
// github.com/duke-git/lancet/v2/xerror.ExampleXError_StackTrace
|
||||
// 52
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleXError_With() {
|
||||
err := New("error").With("level", "high")
|
||||
|
||||
errLevel := err.Values()["level"]
|
||||
|
||||
fmt.Println(errLevel)
|
||||
|
||||
// Output:
|
||||
// high
|
||||
}
|
||||
|
||||
func ExampleXError_Id() {
|
||||
err1 := New("error").Id("e001")
|
||||
err2 := New("error").Id("e001")
|
||||
err3 := New("error").Id("e003")
|
||||
|
||||
equal := err1.Is(err2)
|
||||
notEqual := err1.Is(err3)
|
||||
|
||||
fmt.Println(equal)
|
||||
fmt.Println(notEqual)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleXError_Is() {
|
||||
err1 := New("error").Id("e001")
|
||||
err2 := New("error").Id("e001")
|
||||
err3 := New("error").Id("e003")
|
||||
|
||||
equal := err1.Is(err2)
|
||||
notEqual := err1.Is(err3)
|
||||
|
||||
fmt.Println(equal)
|
||||
fmt.Println(notEqual)
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleXError_Values() {
|
||||
err := New("error").With("level", "high")
|
||||
|
||||
errLevel := err.Values()["level"]
|
||||
|
||||
fmt.Println(errLevel)
|
||||
|
||||
// Output:
|
||||
// high
|
||||
}
|
||||
|
||||
func ExampleXError_Info() {
|
||||
cause := errors.New("error")
|
||||
err := Wrap(cause, "invalid username").Id("e001").With("level", "high")
|
||||
|
||||
errInfo := err.Info()
|
||||
|
||||
fmt.Println(errInfo.Id)
|
||||
fmt.Println(errInfo.Cause)
|
||||
fmt.Println(errInfo.Values["level"])
|
||||
fmt.Println(errInfo.Message)
|
||||
|
||||
// Output:
|
||||
// e001
|
||||
// error
|
||||
// high
|
||||
// invalid username
|
||||
}
|
||||
|
||||
func ExampleTryUnwrap() {
|
||||
result1 := TryUnwrap(strconv.Atoi("42"))
|
||||
fmt.Println(result1)
|
||||
|
||||
_, err := strconv.Atoi("4o2")
|
||||
@@ -17,7 +148,7 @@ func ExampleUnwrap() {
|
||||
fmt.Println(result2)
|
||||
}()
|
||||
|
||||
Unwrap(strconv.Atoi("4o2"))
|
||||
TryUnwrap(strconv.Atoi("4o2"))
|
||||
|
||||
// Output:
|
||||
// 42
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
package xerror
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestUnwrap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestUnwrap")
|
||||
assert.Equal(42, Unwrap(strconv.Atoi("42")))
|
||||
func TestTryUnwrap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestTryUnwrap")
|
||||
assert.Equal(42, TryUnwrap(strconv.Atoi("42")))
|
||||
}
|
||||
|
||||
func TestUnwrapFail(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestUnwrapFail")
|
||||
func TestTryUnwrapFail(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestTryUnwrapFail")
|
||||
|
||||
_, err := strconv.Atoi("4o2")
|
||||
defer func() {
|
||||
@@ -21,5 +23,85 @@ func TestUnwrapFail(t *testing.T) {
|
||||
assert.Equal(err.Error(), v.(*strconv.NumError).Error())
|
||||
}()
|
||||
|
||||
Unwrap(strconv.Atoi("4o2"))
|
||||
TryUnwrap(strconv.Atoi("4o2"))
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestNew")
|
||||
|
||||
err := New("error occurs")
|
||||
assert.Equal("error occurs", err.Error())
|
||||
}
|
||||
|
||||
func TestWrap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestWrap")
|
||||
|
||||
err := New("wrong password")
|
||||
wrapErr := Wrap(err, "error")
|
||||
|
||||
assert.Equal("error: wrong password", wrapErr.Error())
|
||||
}
|
||||
|
||||
func TestXError_Wrap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestXError_Wrap")
|
||||
|
||||
err1 := New("error").With("level", "high")
|
||||
err2 := err1.Wrap(errors.New("bad"))
|
||||
|
||||
assert.Equal("error: bad", err2.Error())
|
||||
}
|
||||
|
||||
func TestXError_Unwrap(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestXError_Unwrap")
|
||||
|
||||
err1 := New("error").With("level", "high")
|
||||
|
||||
err2 := err1.Wrap(errors.New("bad"))
|
||||
|
||||
err := err2.Unwrap()
|
||||
|
||||
assert.Equal("bad", err.Error())
|
||||
}
|
||||
|
||||
func TestXError_StackTrace(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestXError_StackTrace")
|
||||
|
||||
err := New("error")
|
||||
|
||||
stacks := err.Stacks()
|
||||
|
||||
assert.Equal(3, len(stacks))
|
||||
assert.Equal("github.com/duke-git/lancet/v2/xerror.TestXError_StackTrace", stacks[0].Func)
|
||||
assert.Equal(69, stacks[0].Line)
|
||||
assert.Equal(true, strings.Contains(stacks[0].File, "xerror_test.go"))
|
||||
}
|
||||
|
||||
func TestXError_With_Id_Is_Values(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestXError_With_Id_Is_Values")
|
||||
|
||||
baseErr := New("baseError")
|
||||
err1 := New("error1").Id("e001").With("level", "high")
|
||||
err2 := New("error2").Id("e002").With("level", "low")
|
||||
|
||||
err := err1.Wrap(baseErr).With("v", "1.0")
|
||||
|
||||
assert.Equal(true, errors.Is(err, baseErr))
|
||||
assert.NotEqual(err, err1)
|
||||
assert.IsNotNil(err.Values()["v"])
|
||||
assert.IsNil(err1.Values()["v"])
|
||||
|
||||
assert.Equal(false, errors.Is(err, err2))
|
||||
}
|
||||
|
||||
func TestXError_Info(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestXError_Info")
|
||||
|
||||
cause := errors.New("error")
|
||||
err := Wrap(cause, "invalid username").Id("e001").With("level", "high")
|
||||
|
||||
errInfo := err.Info()
|
||||
assert.Equal("invalid username", errInfo.Message)
|
||||
assert.Equal("e001", errInfo.Id)
|
||||
assert.Equal(cause, errInfo.Cause)
|
||||
assert.Equal("high", errInfo.Values["level"])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user