mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-09 15:12:26 +08:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
69
README.md
69
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)
|
||||
@@ -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.6. </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.6 // 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
|
||||
@@ -248,6 +248,10 @@ import "github.com/duke-git/lancet/v2/convertor"
|
||||
[[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)]
|
||||
|
||||
|
||||
### 5. Cryptor package is for data encryption and decryption.
|
||||
|
||||
@@ -604,12 +608,24 @@ 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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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-)]
|
||||
@@ -619,6 +635,18 @@ 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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<big>Transform</big>** : transform a map to another type map.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Transform)]
|
||||
- **<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)]
|
||||
@@ -804,6 +832,8 @@ 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)]
|
||||
- **<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)]
|
||||
@@ -836,13 +866,16 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
|
||||
- **<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)]
|
||||
@@ -855,6 +888,8 @@ 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)]
|
||||
- **<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)]
|
||||
@@ -867,6 +902,8 @@ 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)]
|
||||
- **<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)]
|
||||
@@ -920,12 +957,16 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[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)]
|
||||
@@ -1012,6 +1053,8 @@ 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)]
|
||||
- **<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--)]
|
||||
@@ -1039,6 +1082,10 @@ 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)]
|
||||
- **<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)]
|
||||
|
||||
### 19. System package contain some functions about os, runtime, shell command.
|
||||
|
||||
@@ -1178,31 +1225,43 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
#### Function list:
|
||||
- **<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/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/w84d7Mb3Afk)]
|
||||
[[play](https://go.dev/play/p/acyZVkNZEeW)]
|
||||
|
||||
|
||||
## How to Contribute
|
||||
|
||||
@@ -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)
|
||||
@@ -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.6。</b>
|
||||
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.3.7。</b>
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet@v1.3.6 // 使用go1.18以下版本, 必须安装v1.x.x版本
|
||||
go get github.com/duke-git/lancet@v1.3.7 // 使用go1.18以下版本, 必须安装v1.x.x版本
|
||||
```
|
||||
|
||||
## 用法
|
||||
@@ -247,6 +247,9 @@ import "github.com/duke-git/lancet/v2/convertor"
|
||||
[[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)]
|
||||
|
||||
|
||||
### 5. cryptor 加密包支持数据加密和解密,获取 md5,hash 值。支持 base64, md5, hmac, aes, des, rsa。
|
||||
@@ -608,12 +611,24 @@ 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)]
|
||||
- **<big>FilterByValues</big>** : 迭代map, 返回一个新map,其value都是给定的value值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FilterByValues)]
|
||||
- **<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)]
|
||||
- **<big>OmitByKeys</big>** : FilterByKeys的反向操作, 迭代map, 返回一个新map,其key不包括给定的key值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#OmitByKeys)]
|
||||
- **<big>OmitByValues</big>** : FilterByValues的反向操作, 迭代map, 返回一个新map,其value不包括给定的value值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#OmitByValues)]
|
||||
- **<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)]
|
||||
- **<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-)]
|
||||
@@ -623,7 +638,19 @@ 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>IsDisjoint</big>** : 验证两个 map 是否具有不同的 key。
|
||||
- **<big>ValuesBy</big>** : 创建一个切片,其元素是每个map的value调用mapper函数的结果。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#ValuesBy)]
|
||||
- **<big>MapKeys</big>** : 操作map的每个key,然后转为新的map。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#MapKeys)]
|
||||
- **<big>MapValues</big>** : 操作map的每个value,然后转为新的map。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#MapValues)]
|
||||
- **<big>Entries</big>** : 将map转换为键/值对切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#Entries)]
|
||||
- **<big>FromEntries</big>** : 基于键/值对的切片创建map。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FromEntries)]
|
||||
- **<big>Transform</big>** : 将map转换为其他类型的map。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#Transform)]
|
||||
- **<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)]
|
||||
|
||||
@@ -812,6 +839,8 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<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>ContainBy</big>** : 根据predicate函数判断切片是否包含某个值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ContainBy)]
|
||||
- **<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)]
|
||||
@@ -844,13 +873,16 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
|
||||
- **<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)]
|
||||
@@ -863,6 +895,8 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<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>FilterMap</big>** : 返回一个将filter和map操作应用于给定切片的切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FilterMap)]
|
||||
- **<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)]
|
||||
@@ -875,6 +909,8 @@ 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>FlatMap</big>** : 将切片转换为其它类型切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FlatMap)]
|
||||
- **<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)]
|
||||
@@ -928,12 +964,16 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
[[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)]
|
||||
@@ -1022,6 +1062,8 @@ 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)]
|
||||
- **<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--)]
|
||||
@@ -1049,6 +1091,10 @@ 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)]
|
||||
- **<big>WordCount</big>** : 返回有意义单词的数量,只支持字母字符单词。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#WordCount)]
|
||||
|
||||
### 18. system 包含 os, runtime, shell command 的相关函数。
|
||||
|
||||
@@ -1190,31 +1236,43 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
|
||||
- **<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/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/w84d7Mb3Afk)]
|
||||
[[play](https://go.dev/play/p/acyZVkNZEeW)]
|
||||
|
||||
|
||||
## 如何贡献代码
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
@@ -327,7 +328,7 @@ func DecodeByte(data []byte, target any) error {
|
||||
|
||||
// DeepClone creates a deep copy of passed item.
|
||||
// can't clone unexported field of struct
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/j4DP5dquxnk
|
||||
func DeepClone[T any](src T) T {
|
||||
c := cloner{
|
||||
ptrs: map[reflect.Type]map[uintptr]reflect.Value{},
|
||||
@@ -340,3 +341,46 @@ func DeepClone[T any](src T) T {
|
||||
|
||||
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: todo
|
||||
func CopyProperties[T, U any](dst T, src U) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = errors.New(fmt.Sprintf("%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
|
||||
}
|
||||
|
||||
@@ -295,3 +295,45 @@ func ExampleDeepClone() {
|
||||
// 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{}
|
||||
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}
|
||||
}
|
||||
|
||||
@@ -298,3 +298,53 @@ func TestDeepClone(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
- [EncodeByte](#EncodeByte)
|
||||
- [DecodeByte](#DecodeByte)
|
||||
- [DeepClone](#DeepClone)
|
||||
- [CopyProperties](#CopyProperties)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -651,44 +652,108 @@ import (
|
||||
|
||||
func main() {
|
||||
type Struct struct {
|
||||
Str string
|
||||
Int int
|
||||
Float float64
|
||||
Bool bool
|
||||
Nil interface{}
|
||||
unexported string
|
||||
}
|
||||
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",
|
||||
},
|
||||
}
|
||||
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)
|
||||
for _, item := range cases {
|
||||
cloned := convertor.DeepClone(item)
|
||||
|
||||
isPointerEqual := &cloned == &item
|
||||
fmt.Println(cloned, isPointerEqual)
|
||||
}
|
||||
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
|
||||
// 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}
|
||||
}
|
||||
```
|
||||
@@ -39,6 +39,8 @@ import (
|
||||
- [EncodeByte](#EncodeByte)
|
||||
- [DecodeByte](#DecodeByte)
|
||||
- [DeepClone](#DeepClone)
|
||||
- [CopyProperties](#CopyProperties)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -650,44 +652,107 @@ import (
|
||||
|
||||
func main() {
|
||||
type Struct struct {
|
||||
Str string
|
||||
Int int
|
||||
Float float64
|
||||
Bool bool
|
||||
Nil interface{}
|
||||
unexported string
|
||||
}
|
||||
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",
|
||||
},
|
||||
}
|
||||
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)
|
||||
for _, item := range cases {
|
||||
cloned := convertor.DeepClone(item)
|
||||
|
||||
isPointerEqual := &cloned == &item
|
||||
fmt.Println(cloned, isPointerEqual)
|
||||
}
|
||||
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
|
||||
// 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}
|
||||
}
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
122
docs/slice.md
122
docs/slice.md
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
- [AppendIfAbsent](#AppendIfAbsent)
|
||||
- [Contain](#Contain)
|
||||
- [ContainBy](#ContainBy)
|
||||
- [ContainSubSlice](#ContainSubSlice)
|
||||
- [Chunk](#Chunk)
|
||||
- [Compact](#Compact)
|
||||
@@ -56,6 +57,8 @@ import (
|
||||
- [IndexOf](#IndexOf)
|
||||
- [LastIndexOf](#LastIndexOf)
|
||||
- [Map](#Map)
|
||||
- [FilterMap](#FilterMap)
|
||||
- [FlatMap](#FlatMap)
|
||||
- [Merge](#Merge)
|
||||
- [Reverse](#Reverse)
|
||||
- [Reduce](#Reduce)
|
||||
@@ -149,6 +152,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>
|
||||
@@ -1252,6 +1300,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>
|
||||
@@ -1467,8 +1585,8 @@ func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
result := slice.Shuffle(nums)
|
||||
|
||||
fmt.Println(res)
|
||||
|
||||
fmt.Println(res)
|
||||
|
||||
// Output:
|
||||
// [3 1 5 4 2] (random order)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
- [AppendIfAbsent](#AppendIfAbsent)
|
||||
- [Contain](#Contain)
|
||||
- [ContainBy](#ContainBy)
|
||||
- [ContainSubSlice](#ContainSubSlice)
|
||||
- [Chunk](#Chunk)
|
||||
- [Compact](#Compact)
|
||||
@@ -56,6 +57,8 @@ import (
|
||||
- [IndexOf](#IndexOf)
|
||||
- [LastIndexOf](#LastIndexOf)
|
||||
- [Map](#Map)
|
||||
- [FilterMap](#FilterMap)
|
||||
- [FlatMap](#FlatMap)
|
||||
- [Merge](#Merge)
|
||||
- [Reverse](#Reverse)
|
||||
- [Reduce](#Reduce)
|
||||
@@ -149,6 +152,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>
|
||||
@@ -1254,6 +1302,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>
|
||||
|
||||
138
docs/strutil.md
138
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,8 @@ import (
|
||||
- [Substring](#Substring)
|
||||
- [Wrap](#Wrap)
|
||||
- [Unwrap](#Unwrap)
|
||||
- [SplitWords](#SplitWords)
|
||||
- [WordCount](#WordCount)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -449,6 +452,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 +850,91 @@ 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
|
||||
}
|
||||
```
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
- [UpperKebabCase](#UpperKebabCase)
|
||||
- [LowerFirst](#LowerFirst)
|
||||
- [UpperFirst](#UpperFirst)
|
||||
- [Pad](#Pad)
|
||||
- [PadEnd](#PadEnd)
|
||||
- [PadStart](#PadStart)
|
||||
- [Reverse](#Reverse)
|
||||
@@ -42,6 +43,8 @@ import (
|
||||
- [Substring](#Substring)
|
||||
- [Wrap](#Wrap)
|
||||
- [Unwrap](#Unwrap)
|
||||
- [SplitWords](#SplitWords)
|
||||
- [WordCount](#WordCount)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -449,6 +452,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 +850,90 @@ 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
|
||||
}
|
||||
```
|
||||
@@ -133,7 +133,7 @@ func main() {
|
||||
err1 := xerror.New("error").With("level", "high")
|
||||
wrapErr := errors.Wrap(err1, "oops")
|
||||
|
||||
err := xerror.Unwrap(err1)
|
||||
err := xerror.Unwrap(wrapErr)
|
||||
|
||||
values := err.Values()
|
||||
fmt.Println(values["level"])
|
||||
@@ -441,7 +441,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := New("error")
|
||||
err := xerror.New("error")
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
|
||||
@@ -133,7 +133,7 @@ func main() {
|
||||
err1 := xerror.New("error").With("level", "high")
|
||||
wrapErr := errors.Wrap(err1, "oops")
|
||||
|
||||
err := xerror.Unwrap(err1)
|
||||
err := xerror.Unwrap(wrapErr)
|
||||
|
||||
values := err.Values()
|
||||
fmt.Println(values["level"])
|
||||
@@ -441,7 +441,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := New("error")
|
||||
err := xerror.New("error")
|
||||
fmt.Println(err.Error())
|
||||
|
||||
// Output:
|
||||
|
||||
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: todo
|
||||
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: todo
|
||||
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: todo
|
||||
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: todo
|
||||
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: todo
|
||||
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: todo
|
||||
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: todo
|
||||
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: todo
|
||||
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: todo
|
||||
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: todo
|
||||
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: todo
|
||||
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: todo
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ const (
|
||||
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.
|
||||
@@ -81,10 +88,12 @@ func RandNumeralOrLetter(length int) string {
|
||||
// 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)
|
||||
|
||||
@@ -22,7 +22,7 @@ var (
|
||||
)
|
||||
|
||||
// Contain check if the target value is in the slice or not.
|
||||
// Play: https://go.dev/play/p/_454yEHcNjf
|
||||
// Play: todo
|
||||
func Contain[T comparable](slice []T, target T) bool {
|
||||
for _, item := range slice {
|
||||
if item == target {
|
||||
@@ -33,6 +33,17 @@ func Contain[T comparable](slice []T, target T) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainBy returns true if predicate function return true.
|
||||
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 {
|
||||
@@ -421,6 +432,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: todo
|
||||
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: todo
|
||||
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,7 +599,7 @@ func DeleteAt[T any](slice []T, start int, end ...int) []T {
|
||||
}
|
||||
|
||||
// Drop drop n elements from the start of a slice.
|
||||
// Play: https://go.dev/play/p/pJ-d6MUWcvK
|
||||
// Play: https://go.dev/play/p/jnPO2yQsT8H
|
||||
func Drop[T any](slice []T, n int) []T {
|
||||
size := len(slice)
|
||||
|
||||
@@ -577,7 +617,7 @@ func Drop[T any](slice []T, n int) []T {
|
||||
}
|
||||
|
||||
// DropRight drop n elements from the end of a slice.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/8bcXvywZezG
|
||||
func DropRight[T any](slice []T, n int) []T {
|
||||
size := len(slice)
|
||||
|
||||
@@ -595,7 +635,7 @@ func DropRight[T any](slice []T, n int) []T {
|
||||
}
|
||||
|
||||
// DropWhile drop n elements from the start of a slice while predicate function returns true.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/4rt252UV_qs
|
||||
func DropWhile[T any](slice []T, predicate func(item T) bool) []T {
|
||||
i := 0
|
||||
|
||||
@@ -611,7 +651,7 @@ func DropWhile[T any](slice []T, predicate func(item T) bool) []T {
|
||||
}
|
||||
|
||||
// DropRightWhile drop n elements from the end of a slice while predicate function returns true.
|
||||
// Play: todo
|
||||
// Play: https://go.dev/play/p/6wyK3zMY56e
|
||||
func DropRightWhile[T any](slice []T, predicate func(item T) bool) []T {
|
||||
i := len(slice) - 1
|
||||
|
||||
@@ -831,7 +871,7 @@ func Shuffle[T any](slice []T) []T {
|
||||
}
|
||||
|
||||
// IsAscending checks if a slice is ascending order.
|
||||
// Play: todo
|
||||
// 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] {
|
||||
@@ -843,7 +883,7 @@ func IsAscending[T constraints.Ordered](slice []T) bool {
|
||||
}
|
||||
|
||||
// IsDescending checks if a slice is descending order.
|
||||
// Play: todo
|
||||
// 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] {
|
||||
@@ -855,13 +895,13 @@ func IsDescending[T constraints.Ordered](slice []T) bool {
|
||||
}
|
||||
|
||||
// IsSorted checks if a slice is sorted(ascending or descending).
|
||||
// Play: todo
|
||||
// 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: todo
|
||||
// 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)
|
||||
|
||||
|
||||
@@ -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"})
|
||||
@@ -367,6 +394,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}
|
||||
|
||||
|
||||
@@ -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"}))
|
||||
@@ -315,6 +338,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{
|
||||
{},
|
||||
|
||||
@@ -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: todo
|
||||
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,76 @@ 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: todo
|
||||
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: todo
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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,53 @@ 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
|
||||
}
|
||||
|
||||
@@ -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,37 @@ 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))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user