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

Compare commits

...

11 Commits

Author SHA1 Message Date
dudaodong
ef1e548dfc merge main 2023-03-13 19:44:44 +08:00
zm
924589d2da Add StructUtil for provide more rich functions (#79)
* add support json tag attribute for StructToMap function

* add the structutil to provide more rich functions and fixed #77
2023-03-13 19:28:37 +08:00
dudaodong
77f32f4cc6 Merge branch 'main' into v2 2023-03-11 20:14:32 +08:00
zm
1755dd249b add support json tag attribute for StructToMap function (#78) 2023-03-11 20:08:41 +08:00
dudaodong
7a25688ec1 doc: add doc for Range and RangeWithStep 2023-03-09 17:13:51 +08:00
dudaodong
51a6912eb3 feat: add RangeWithStep function 2023-03-06 18:05:58 +08:00
dudaodong
28d0428b50 feat: add Range function 2023-03-06 17:49:56 +08:00
dudaodong
6a9eb645bb fix: fix StructToUrlValues failed when tag contain omitempty 2023-03-06 17:07:57 +08:00
dudaodong
3857b342f6 fix: fix ExampleContext failed 2023-03-01 11:45:02 +08:00
dudaodong
71aa91a58d doc: add go playground demo 2023-03-01 11:39:27 +08:00
dudaodong
081908bce3 fix: fix goline error 2023-03-01 11:10:12 +08:00
24 changed files with 844 additions and 311 deletions

View File

@@ -251,7 +251,7 @@ import "github.com/duke-git/lancet/v2/convertor"
[[play](https://go.dev/play/p/j4DP5dquxnk)] [[play](https://go.dev/play/p/j4DP5dquxnk)]
- **<big>CopyProperties</big>** : copies each field from the source struct into the destination struct. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/convertor.md#CopyProperties)]
[[play](https://go.dev/play/p/FOVY3XJL-6B)]
### 5. Cryptor package is for data encryption and decryption. ### 5. Cryptor package is for data encryption and decryption.
@@ -456,9 +456,6 @@ import "github.com/duke-git/lancet/v2/datetime"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#ToIso8601)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime.md#ToIso8601)]
[[play](https://go.dev/play/p/mkhOHQkdeA2)] [[play](https://go.dev/play/p/mkhOHQkdeA2)]
### 7. Datastructure package constains some common data structure. eg. list, linklist, stack, queue, set, tree, graph. ### 7. Datastructure package constains some common data structure. eg. list, linklist, stack, queue, set, tree, graph.
```go ```go
@@ -593,7 +590,6 @@ import "github.com/duke-git/lancet/v2/function"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function.md#Watcher)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/function.md#Watcher)]
[[play](https://go.dev/play/p/l2yrOpCLd1I)] [[play](https://go.dev/play/p/l2yrOpCLd1I)]
### 11. Maputil package includes some functions to manipulate map. ### 11. Maputil package includes some functions to manipulate map.
```go ```go
@@ -610,14 +606,19 @@ import "github.com/duke-git/lancet/v2/maputil"
[[play](https://go.dev/play/p/fSvF3wxuNG7)] [[play](https://go.dev/play/p/fSvF3wxuNG7)]
- **<big>FilterByKeys</big>** : iterates over map, return a new map whose keys are all given keys - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#FilterByKeys)]
[[play](https://go.dev/play/p/7ov6BJHbVqh)]
- **<big>FilterByValues</big>** : iterates over map, return a new map whose values are all given values. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#FilterByValues)]
[[play](https://go.dev/play/p/P3-9MdcXegR)]
- **<big>OmitBy</big>** : the opposite of Filter, removes all the map elements for which the predicate function returns true. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#OmitBy)]
[[play](https://go.dev/play/p/YJM4Hj5hNwm)]
- **<big>OmitByKeys</big>** : the opposite of FilterByKeys, extracts all the map elements which keys are not omitted. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#OmitByKeys)]
[[play](https://go.dev/play/p/jXGrWDBfSRp)]
- **<big>OmitByValues</big>** : the opposite of FilterByValues. remov all elements whose value are in the give slice. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#OmitByValues)]
[[play](https://go.dev/play/p/XB7Y10uw20_U)]
- **<big>Intersect</big>** : iterates over maps, return a new map of key and value pairs in all given maps. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Intersect)]
[[play](https://go.dev/play/p/Zld0oj3sjcC)] [[play](https://go.dev/play/p/Zld0oj3sjcC)]
@@ -626,6 +627,7 @@ import "github.com/duke-git/lancet/v2/maputil"
[[play](https://go.dev/play/p/xNB5bTb97Wd)] [[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. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#KeysBy)]
[[play](https://go.dev/play/p/hI371iB8Up8)]
- **<big>Merge</big>** : merge maps, next key will overwrite previous key. - **<big>Merge</big>** : merge maps, next key will overwrite previous key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Merge)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Merge)]
[[play](https://go.dev/play/p/H95LENF1uB-)] [[play](https://go.dev/play/p/H95LENF1uB-)]
@@ -637,16 +639,22 @@ import "github.com/duke-git/lancet/v2/maputil"
[[play](https://go.dev/play/p/CBKdUc5FTW6)] [[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. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#ValuesBy)]
[[play](https://go.dev/play/p/sg9-oRidh8f)]
- **<big>MapKeys</big>** : transforms a map to other type map by manipulating it's keys. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#MapKeys)]
[[play](https://go.dev/play/p/8scDxWeBDKd)]
- **<big>MapValues</big>** : transforms a map to other type map by manipulating it's values. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#MapValues)]
[[play](https://go.dev/play/p/g92aY3fc7Iw)]
- **<big>Entries</big>** : transforms a map into array of key/value pairs. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Entries)]
[[play](https://go.dev/play/p/Ltb11LNcElY)]
- **<big>FromEntries</big>** : creates a map based on a slice of key/value pairs. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#FromEntries)]
[[play](https://go.dev/play/p/fTdu4sCNjQO)]
- **<big>Transform</big>** : transform a map to another type map. - **<big>Transform</big>** : transform a map to another type map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Transform)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#Transform)]
[[play](https://go.dev/play/p/P6ovfToM3zj)]
- **<big>IsDisjoint</big>** : check two map are disjoint if they have no keys in common. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil.md#IsDisjoint)]
[[play](https://go.dev/play/p/N9qgYg_Ho6f)] [[play](https://go.dev/play/p/N9qgYg_Ho6f)]
@@ -759,8 +767,6 @@ import "github.com/duke-git/lancet/v2/netutil"
- **<big>ParseHttpResponse</big>** : decode http response into target object. - **<big>ParseHttpResponse</big>** : decode http response into target object.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#ParseHttpResponse)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil.md#ParseHttpResponse)]
### 14. Random package implements some basic functions to generate random int and string. ### 14. Random package implements some basic functions to generate random int and string.
```go ```go
@@ -834,6 +840,7 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/_454yEHcNjf)] [[play](https://go.dev/play/p/_454yEHcNjf)]
- **<big>ContainBy</big>** : returns true if predicate function return true. - **<big>ContainBy</big>** : returns true if predicate function return true.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ContainBy)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ContainBy)]
[[play](https://go.dev/play/p/49tkHfX4GNc)]
- **<big>ContainSubSlice</big>** : check if the slice contain a given subslice or not. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ContainSubSlice)]
[[play](https://go.dev/play/p/bcuQ3UT6Sev)] [[play](https://go.dev/play/p/bcuQ3UT6Sev)]
@@ -890,6 +897,7 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/SdPna-7qK4T)] [[play](https://go.dev/play/p/SdPna-7qK4T)]
- **<big>FilterMap</big>** : returns a slice which apply both filtering and mapping to the given slice. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#FilterMap)]
[[play](https://go.dev/play/p/J94SZ_9MiIe)]
- **<big>Find</big>** : iterates over elements of slice, returning the first one that passes a truth test on predicate function. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#Find)]
[[play](https://go.dev/play/p/CBKeBoHVLgq)] [[play](https://go.dev/play/p/CBKeBoHVLgq)]
@@ -904,6 +912,7 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/yjYNHPyCFaF)] [[play](https://go.dev/play/p/yjYNHPyCFaF)]
- **<big>FlatMap</big>** : manipulates a slice and transforms and flattens it to a slice of another type. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#FlatMap)]
[[play](https://go.dev/play/p/_QARWlWs1N_F)]
- **<big>ForEach</big>** : iterates over elements of slice and invokes function for each element. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice.md#ForEach)]
[[play](https://go.dev/play/p/DrPaa4YsHRF)] [[play](https://go.dev/play/p/DrPaa4YsHRF)]
@@ -1055,6 +1064,7 @@ import "github.com/duke-git/lancet/v2/strutil"
[[play](https://go.dev/play/p/sBbBxRbs8MM)] [[play](https://go.dev/play/p/sBbBxRbs8MM)]
- **<big>Pad</big>** : pads string on the left and right side if it's shorter than size. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#Pad)]
[[play](https://go.dev/play/p/NzImQq-VF8q)]
- **<big>PadEnd</big>** : pads string with given characters on the right side if it's shorter than limit size. Padding characters are truncated if they exceed size. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#PadEnd)]
[[play](https://go.dev/play/p/9xP8rN0vz--)] [[play](https://go.dev/play/p/9xP8rN0vz--)]
@@ -1084,8 +1094,10 @@ import "github.com/duke-git/lancet/v2/strutil"
[[play](https://go.dev/play/p/Ec2q4BzCpG-)] [[play](https://go.dev/play/p/Ec2q4BzCpG-)]
- **<big>SplitWords</big>** : splits a string into words, word only contains alphabetic characters. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#SplitWords)]
[[play](https://go.dev/play/p/KLiX4WiysMM)]
- **<big>WordCount</big>** : return the number of meaningful word of a string, word only contains alphabetic characters. - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil.md#WordCount)]
[[play](https://go.dev/play/p/bj7_odx3vRf)]
### 19. System package contain some functions about os, runtime, shell command. ### 19. System package contain some functions about os, runtime, shell command.
@@ -1223,6 +1235,7 @@ import "github.com/duke-git/lancet/v2/xerror"
``` ```
#### Function list: #### Function list:
- **<big>New</big>** : creates a new XError pointer instance with message. - **<big>New</big>** : creates a new XError pointer instance with message.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#New)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#New)]
[[play](https://go.dev/play/p/w4oWZts7q7f)] [[play](https://go.dev/play/p/w4oWZts7q7f)]
@@ -1263,7 +1276,6 @@ import "github.com/duke-git/lancet/v2/xerror"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#TryUnwrap)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror.md#TryUnwrap)]
[[play](https://go.dev/play/p/acyZVkNZEeW)] [[play](https://go.dev/play/p/acyZVkNZEeW)]
## How to Contribute ## How to Contribute
I really appreciate any code commits which make lancet lib powerful. Please follow the rules below to create your pull request. I really appreciate any code commits which make lancet lib powerful. Please follow the rules below to create your pull request.

View File

@@ -250,7 +250,7 @@ import "github.com/duke-git/lancet/v2/convertor"
[[play](https://go.dev/play/p/j4DP5dquxnk)] [[play](https://go.dev/play/p/j4DP5dquxnk)]
- **<big>CopyProperties</big>** : 拷贝不同结构体之间的同名字段。 - **<big>CopyProperties</big>** : 拷贝不同结构体之间的同名字段。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#CopyProperties)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/convertor_zh-CN.md#CopyProperties)]
[[play](https://go.dev/play/p/FOVY3XJL-6B)]
### 5. cryptor 加密包支持数据加密和解密,获取 md5hash 值。支持 base64, md5, hmac, aes, des, rsa。 ### 5. cryptor 加密包支持数据加密和解密,获取 md5hash 值。支持 base64, md5, hmac, aes, des, rsa。
@@ -356,8 +356,6 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#RsaDecrypt)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/cryptor_zh-CN.md#RsaDecrypt)]
[[play](https://go.dev/play/p/uef0q1fz53I)] [[play](https://go.dev/play/p/uef0q1fz53I)]
### 6. datetime 日期时间处理包,格式化日期,比较日期。 ### 6. datetime 日期时间处理包,格式化日期,比较日期。
```go ```go
@@ -457,8 +455,6 @@ import "github.com/duke-git/lancet/v2/datetime"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#ToIso8601)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/datetime_zh-CN.md#ToIso8601)]
[[play](https://go.dev/play/p/mkhOHQkdeA2)] [[play](https://go.dev/play/p/mkhOHQkdeA2)]
### 7. datastructure 包含一些普通的数据结构实现。例如list, linklist, stack, queue, set, tree, graph. ### 7. datastructure 包含一些普通的数据结构实现。例如list, linklist, stack, queue, set, tree, graph.
```go ```go
@@ -491,9 +487,6 @@ import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
- **<big>Hashmap</big>** : 哈希映射。 - **<big>Hashmap</big>** : 哈希映射。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/datastructure/hashmap_zh-CN.md)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/datastructure/hashmap_zh-CN.md)]
### 8. fileutil 包含文件基本操作。 ### 8. fileutil 包含文件基本操作。
```go ```go
@@ -596,7 +589,6 @@ import "github.com/duke-git/lancet/v2/function"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Watcher)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/function_zh-CN.md#Watcher)]
[[play](https://go.dev/play/p/l2yrOpCLd1I)] [[play](https://go.dev/play/p/l2yrOpCLd1I)]
### 11. maputil 包括一些操作 map 的函数. ### 11. maputil 包括一些操作 map 的函数.
```go ```go
@@ -613,14 +605,19 @@ import "github.com/duke-git/lancet/v2/maputil"
[[play](https://go.dev/play/p/fSvF3wxuNG7)] [[play](https://go.dev/play/p/fSvF3wxuNG7)]
- **<big>FilterByKeys</big>** : 迭代 map, 返回一个新 map其 key 都是给定的 key 值。 - **<big>FilterByKeys</big>** : 迭代 map, 返回一个新 map其 key 都是给定的 key 值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FilterByKeys)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FilterByKeys)]
[[play](https://go.dev/play/p/7ov6BJHbVqh)]
- **<big>FilterByValues</big>** : 迭代 map, 返回一个新 map其 value 都是给定的 value 值。 - **<big>FilterByValues</big>** : 迭代 map, 返回一个新 map其 value 都是给定的 value 值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FilterByValues)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FilterByValues)]
[[play](https://go.dev/play/p/P3-9MdcXegR)]
- **<big>OmitBy</big>** : Filter 的反向操作, 迭代 map 中的每对 key 和 value, 删除符合 predicate 函数的 key, value, 返回新 map。 - **<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)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#OmitBy)]
[[play](https://go.dev/play/p/YJM4Hj5hNwm)]
- **<big>OmitByKeys</big>** : FilterByKeys 的反向操作, 迭代 map, 返回一个新 map其 key 不包括给定的 key 值。 - **<big>OmitByKeys</big>** : FilterByKeys 的反向操作, 迭代 map, 返回一个新 map其 key 不包括给定的 key 值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#OmitByKeys)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#OmitByKeys)]
[[play](https://go.dev/play/p/jXGrWDBfSRp)]
- **<big>OmitByValues</big>** : FilterByValues 的反向操作, 迭代 map, 返回一个新 map其 value 不包括给定的 value 值。 - **<big>OmitByValues</big>** : FilterByValues 的反向操作, 迭代 map, 返回一个新 map其 value 不包括给定的 value 值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#OmitByValues)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#OmitByValues)]
[[play](https://go.dev/play/p/XB7Y10uw20_U)]
- **<big>Intersect</big>** : 多个 map 的交集操作。 - **<big>Intersect</big>** : 多个 map 的交集操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#Intersect)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#Intersect)]
[[play](https://go.dev/play/p/Zld0oj3sjcC)] [[play](https://go.dev/play/p/Zld0oj3sjcC)]
@@ -629,6 +626,7 @@ import "github.com/duke-git/lancet/v2/maputil"
[[play](https://go.dev/play/p/xNB5bTb97Wd)] [[play](https://go.dev/play/p/xNB5bTb97Wd)]
- **<big>KeysBy</big>** : 创建一个切片,其元素是每个 map 的 key 调用 mapper 函数的结果。 - **<big>KeysBy</big>** : 创建一个切片,其元素是每个 map 的 key 调用 mapper 函数的结果。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#KeysBy)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#KeysBy)]
[[play](https://go.dev/play/p/hI371iB8Up8)]
- **<big>Merge</big>** : 合并多个 map, 相同的 key 会被之后的 key 覆盖。 - **<big>Merge</big>** : 合并多个 map, 相同的 key 会被之后的 key 覆盖。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#Merge)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#Merge)]
[[play](https://go.dev/play/p/H95LENF1uB-)] [[play](https://go.dev/play/p/H95LENF1uB-)]
@@ -640,16 +638,22 @@ import "github.com/duke-git/lancet/v2/maputil"
[[play](https://go.dev/play/p/CBKdUc5FTW6)] [[play](https://go.dev/play/p/CBKdUc5FTW6)]
- **<big>ValuesBy</big>** : 创建一个切片,其元素是每个 map 的 value 调用 mapper 函数的结果。 - **<big>ValuesBy</big>** : 创建一个切片,其元素是每个 map 的 value 调用 mapper 函数的结果。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#ValuesBy)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#ValuesBy)]
[[play](https://go.dev/play/p/sg9-oRidh8f)]
- **<big>MapKeys</big>** : 操作 map 的每个 key然后转为新的 map。 - **<big>MapKeys</big>** : 操作 map 的每个 key然后转为新的 map。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#MapKeys)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#MapKeys)]
[[play](https://go.dev/play/p/8scDxWeBDKd)]
- **<big>MapValues</big>** : 操作 map 的每个 value然后转为新的 map。 - **<big>MapValues</big>** : 操作 map 的每个 value然后转为新的 map。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#MapValues)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#MapValues)]
[[play](https://go.dev/play/p/g92aY3fc7Iw)]
- **<big>Entries</big>** : 将 map 转换为键/值对切片。 - **<big>Entries</big>** : 将 map 转换为键/值对切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#Entries)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#Entries)]
[[play](https://go.dev/play/p/Ltb11LNcElY)]
- **<big>FromEntries</big>** : 基于键/值对的切片创建 map。 - **<big>FromEntries</big>** : 基于键/值对的切片创建 map。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FromEntries)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#FromEntries)]
[[play](https://go.dev/play/p/fTdu4sCNjQO)]
- **<big>Transform</big>** : 将 map 转换为其他类型的 map。 - **<big>Transform</big>** : 将 map 转换为其他类型的 map。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#Transform)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN#Transform)]
[[play](https://go.dev/play/p/P6ovfToM3zj)]
- **<big>IsDisjoint</big>** : 验证两个 map 是否具有不同的 key。 - **<big>IsDisjoint</big>** : 验证两个 map 是否具有不同的 key。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#IsDisjoint)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/maputil_zh-CN.md#IsDisjoint)]
[[play](https://go.dev/play/p/N9qgYg_Ho6f)] [[play](https://go.dev/play/p/N9qgYg_Ho6f)]
@@ -762,9 +766,6 @@ import "github.com/duke-git/lancet/v2/netutil"
- **<big>ParseHttpResponse</big>** : 解析 http 响应体到目标结构体。 - **<big>ParseHttpResponse</big>** : 解析 http 响应体到目标结构体。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#ParseHttpResponse)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/netutil_zh-CN.md#ParseHttpResponse)]
### 14. random 随机数生成器包,可以生成随机[]bytes, int, string。 ### 14. random 随机数生成器包,可以生成随机[]bytes, int, string。
```go ```go
@@ -798,8 +799,6 @@ import "github.com/duke-git/lancet/v2/random"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/random_zh-CN.md#UUIdV4)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/random_zh-CN.md#UUIdV4)]
[[play](https://go.dev/play/p/_Z9SFmr28ft)] [[play](https://go.dev/play/p/_Z9SFmr28ft)]
### 15. retry 重试执行函数直到函数运行成功或被 context cancel。 ### 15. retry 重试执行函数直到函数运行成功或被 context cancel。
```go ```go
@@ -824,8 +823,6 @@ import "github.com/duke-git/lancet/v2/retry"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#RetryTimes)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/retry_zh-CN.md#RetryTimes)]
[[play](https://go.dev/play/p/ssfVeU2SwLO)] [[play](https://go.dev/play/p/ssfVeU2SwLO)]
### 16. slice 包含操作切片的方法集合。 ### 16. slice 包含操作切片的方法集合。
```go ```go
@@ -833,6 +830,7 @@ import "github.com/duke-git/lancet/v2/slice"
``` ```
#### 函数列表: #### 函数列表:
- **<big>AppendIfAbsent</big>** : 当前切片中不包含值时,将该值追加到切片中。 - **<big>AppendIfAbsent</big>** : 当前切片中不包含值时,将该值追加到切片中。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#AppendIfAbsent)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#AppendIfAbsent)]
[[play](https://go.dev/play/p/GNdv7Jg2Taj)] [[play](https://go.dev/play/p/GNdv7Jg2Taj)]
@@ -841,6 +839,7 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/_454yEHcNjf)] [[play](https://go.dev/play/p/_454yEHcNjf)]
- **<big>ContainBy</big>** : 根据 predicate 函数判断切片是否包含某个值。 - **<big>ContainBy</big>** : 根据 predicate 函数判断切片是否包含某个值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ContainBy)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ContainBy)]
[[play](https://go.dev/play/p/49tkHfX4GNc)]
- **<big>ContainSubSlice</big>** : 判断 slice 是否包含 subslice。 - **<big>ContainSubSlice</big>** : 判断 slice 是否包含 subslice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ContainSubSlice)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ContainSubSlice)]
[[play](https://go.dev/play/p/bcuQ3UT6Sev)] [[play](https://go.dev/play/p/bcuQ3UT6Sev)]
@@ -897,6 +896,7 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/SdPna-7qK4T)] [[play](https://go.dev/play/p/SdPna-7qK4T)]
- **<big>FilterMap</big>** : 返回一个将 filter 和 map 操作应用于给定切片的切片。 - **<big>FilterMap</big>** : 返回一个将 filter 和 map 操作应用于给定切片的切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FilterMap)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FilterMap)]
[[play](https://go.dev/play/p/J94SZ_9MiIe)]
- **<big>Find</big>** : 遍历切片的元素,返回第一个通过 predicate 函数真值测试的元素。 - **<big>Find</big>** : 遍历切片的元素,返回第一个通过 predicate 函数真值测试的元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Find)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#Find)]
[[play](https://go.dev/play/p/CBKeBoHVLgq)] [[play](https://go.dev/play/p/CBKeBoHVLgq)]
@@ -911,6 +911,7 @@ import "github.com/duke-git/lancet/v2/slice"
[[play](https://go.dev/play/p/yjYNHPyCFaF)] [[play](https://go.dev/play/p/yjYNHPyCFaF)]
- **<big>FlatMap</big>** : 将切片转换为其它类型切片。 - **<big>FlatMap</big>** : 将切片转换为其它类型切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FlatMap)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#FlatMap)]
[[play](https://go.dev/play/p/_QARWlWs1N_F)]
- **<big>ForEach</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数。 - **<big>ForEach</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ForEach)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#ForEach)]
[[play](https://go.dev/play/p/DrPaa4YsHRF)] [[play](https://go.dev/play/p/DrPaa4YsHRF)]
@@ -1020,7 +1021,6 @@ import "github.com/duke-git/lancet/v2/slice"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#KeyBy)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/slice_zh-CN.md#KeyBy)]
[[play](https://go.dev/play/p/uXod2LWD1Kg)] [[play](https://go.dev/play/p/uXod2LWD1Kg)]
### 17. strutil 包含字符串处理的相关函数。 ### 17. strutil 包含字符串处理的相关函数。
```go ```go
@@ -1064,6 +1064,7 @@ import "github.com/duke-git/lancet/v2/strutil"
[[play](https://go.dev/play/p/sBbBxRbs8MM)] [[play](https://go.dev/play/p/sBbBxRbs8MM)]
- **<big>Pad</big>** : 如果字符串长度短于 size则在左右两侧填充字符串。 - **<big>Pad</big>** : 如果字符串长度短于 size则在左右两侧填充字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Pad)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#Pad)]
[[play](https://go.dev/play/p/NzImQq-VF8q)]
- **<big>PadEnd</big>** : 如果字符串短于限制大小,则在右侧用给定字符填充字符串。 如果填充字符超出大小,它们将被截断。 - **<big>PadEnd</big>** : 如果字符串短于限制大小,则在右侧用给定字符填充字符串。 如果填充字符超出大小,它们将被截断。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadEnd)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#PadEnd)]
[[play](https://go.dev/play/p/9xP8rN0vz--)] [[play](https://go.dev/play/p/9xP8rN0vz--)]
@@ -1093,8 +1094,11 @@ import "github.com/duke-git/lancet/v2/strutil"
[[play](https://go.dev/play/p/Ec2q4BzCpG-)] [[play](https://go.dev/play/p/Ec2q4BzCpG-)]
- **<big>SplitWords</big>** : 将字符串拆分为单词,只支持字母字符单词。 - **<big>SplitWords</big>** : 将字符串拆分为单词,只支持字母字符单词。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#SplitWords)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#SplitWords)]
[[play](https://go.dev/play/p/KLiX4WiysMM)]
- **<big>WordCount</big>** : 返回有意义单词的数量,只支持字母字符单词。 - **<big>WordCount</big>** : 返回有意义单词的数量,只支持字母字符单词。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#WordCount)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/strutil_zh-CN.md#WordCount)]
[[play](https://go.dev/play/p/bj7_odx3vRf)]
### 18. system 包含 os, runtime, shell command 的相关函数。 ### 18. system 包含 os, runtime, shell command 的相关函数。
@@ -1225,7 +1229,6 @@ import "github.com/duke-git/lancet/v2/validator"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsGBK)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/validator_zh-CN.md#IsGBK)]
[[play](https://go.dev/play/p/E2nt3unlmzP)] [[play](https://go.dev/play/p/E2nt3unlmzP)]
### 20. xerror 包实现一些错误处理函数 ### 20. xerror 包实现一些错误处理函数
```go ```go
@@ -1274,7 +1277,6 @@ import "github.com/duke-git/lancet/v2/xerror"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#TryUnwrap)] [[doc](https://github.com/duke-git/lancet/blob/main/docs/xerror_zh-CN.md#TryUnwrap)]
[[play](https://go.dev/play/p/acyZVkNZEeW)] [[play](https://go.dev/play/p/acyZVkNZEeW)]
## 如何贡献代码 ## 如何贡献代码
非常感激任何的代码提交以使 lancet 的功能越来越强大。创建 pull request 时请遵守以下规则。 非常感激任何的代码提交以使 lancet 的功能越来越强大。创建 pull request 时请遵守以下规则。

View File

@@ -11,9 +11,9 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/duke-git/lancet/v2/structutil"
"math" "math"
"reflect" "reflect"
"regexp"
"strconv" "strconv"
"strings" "strings"
) )
@@ -235,31 +235,7 @@ func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K
// map key is specified same as struct field tag `json` value. // map key is specified same as struct field tag `json` value.
// Play: https://go.dev/play/p/KYGYJqNUBOI // Play: https://go.dev/play/p/KYGYJqNUBOI
func StructToMap(value any) (map[string]any, error) { func StructToMap(value any) (map[string]any, error) {
v := reflect.ValueOf(value) return structutil.ToMap(value)
t := reflect.TypeOf(value)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", value)
}
result := make(map[string]any)
fieldNum := t.NumField()
pattern := `^[A-Z]`
regex := regexp.MustCompile(pattern)
for i := 0; i < fieldNum; i++ {
name := t.Field(i).Name
tag := t.Field(i).Tag.Get("json")
if regex.MatchString(name) && tag != "" {
//result[name] = v.Field(i).Interface()
result[tag] = v.Field(i).Interface()
}
}
return result, nil
} }
// MapToSlice convert map to slice based on iteratee function. // MapToSlice convert map to slice based on iteratee function.
@@ -343,11 +319,11 @@ func DeepClone[T any](src T) T {
} }
// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers. // CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers.
// Play: todo // Play: https://go.dev/play/p/FOVY3XJL-6B
func CopyProperties[T, U any](dst T, src U) (err error) { func CopyProperties[T, U any](dst T, src U) (err error) {
defer func() { defer func() {
if e := recover(); e != nil { if e := recover(); e != nil {
err = errors.New(fmt.Sprintf("%v", e)) err = fmt.Errorf("%v", e)
} }
}() }()

View File

@@ -260,7 +260,7 @@ func ExampleDeepClone() {
Float float64 Float float64
Bool bool Bool bool
Nil interface{} Nil interface{}
unexported string // unexported string
} }
cases := []interface{}{ cases := []interface{}{
@@ -323,12 +323,18 @@ func ExampleCopyProperties() {
user := User{Name: "user001", Age: 10, Role: "Admin", Addr: Address{Country: "CN", ZipCode: "001"}, Hobbys: []string{"a", "b"}, salary: 1000} user := User{Name: "user001", Age: 10, Role: "Admin", Addr: Address{Country: "CN", ZipCode: "001"}, Hobbys: []string{"a", "b"}, salary: 1000}
employee1 := Employee{} employee1 := Employee{}
CopyProperties(&employee1, &user) err := CopyProperties(&employee1, &user)
if err != nil {
return
}
employee2 := Employee{Name: "employee001", Age: 20, Role: "User", employee2 := Employee{Name: "employee001", Age: 20, Role: "User",
Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500} Addr: Address{Country: "UK", ZipCode: "002"}, salary: 500}
CopyProperties(&employee2, &user) err = CopyProperties(&employee2, &user)
if err != nil {
return
}
fmt.Println(employee1) fmt.Println(employee1)
fmt.Println(employee2) fmt.Println(employee2)

View File

@@ -180,6 +180,7 @@ func TestToMap(t *testing.T) {
func TestStructToMap(t *testing.T) { func TestStructToMap(t *testing.T) {
assert := internal.NewAssert(t, "TestStructToMap") assert := internal.NewAssert(t, "TestStructToMap")
t.Run("StructToMap", func(_ *testing.T) {
type People struct { type People struct {
Name string `json:"name"` Name string `json:"name"`
age int age int
@@ -189,9 +190,26 @@ func TestStructToMap(t *testing.T) {
100, 100,
} }
pm, _ := StructToMap(p) pm, _ := StructToMap(p)
var expected = map[string]any{"name": "test"}
expected := map[string]any{"name": "test"}
assert.Equal(expected, pm) assert.Equal(expected, pm)
})
t.Run("StructToMapWithJsonAttr", func(_ *testing.T) {
type People struct {
Name string `json:"name,omitempty"` // json tag with attribute
Phone string `json:"phone"` // json tag without attribute
Sex string `json:"-"` // ignore
age int // no tag
}
p := People{
Phone: "1111",
Sex: "male",
age: 100,
}
pm, _ := StructToMap(p)
var expected = map[string]any{"phone": "1111"}
assert.Equal(expected, pm)
})
} }
func TestMapToSlice(t *testing.T) { func TestMapToSlice(t *testing.T) {
@@ -264,7 +282,7 @@ func TestDeepClone(t *testing.T) {
Float float64 Float float64
Bool bool Bool bool
Nil interface{} Nil interface{}
unexported string // unexported string
} }
cases := []interface{}{ cases := []interface{}{

View File

@@ -34,6 +34,8 @@ import (
- [RoundToFloat](#RoundToFloat) - [RoundToFloat](#RoundToFloat)
- [RoundToString](#RoundToString) - [RoundToString](#RoundToString)
- [TruncRound](#TruncRound) - [TruncRound](#TruncRound)
- [Range](#Range)
- [RangeWithStep](#RangeWithStep)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -476,3 +478,81 @@ func main() {
// 0.125 // 0.125
} }
``` ```
### <span id="Range">Range</span>
<p>Creates a slice of numbers from start with specified count, element step is 1.</p>
<b>Signature:</b>
```go
func Range[T constraints.Integer | constraints.Float](start T, count int) []T
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Range(1, 4)
result2 := mathutil.Range(1, -4)
result3 := mathutil.Range(-4, 4)
result4 := mathutil.Range(1.0, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// [1 2 3 4]
// [1 2 3 4]
// [-4 -3 -2 -1]
// [1 2 3 4]
}
```
### <span id="RangeWithStep">RangeWithStep</span>
<p>Creates a slice of numbers from start to end with specified step.</p>
<b>Signature:</b>
```go
func RangeWithStep[T constraints.Integer | constraints.Float](start, end, step T) []T
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.RangeWithStep(1, 4, 1)
result2 := mathutil.RangeWithStep(1, -1, 0)
result3 := mathutil.RangeWithStep(-4, 1, 2)
result4 := mathutil.RangeWithStep(1.0, 4.0, 1.1)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// [1 2 3]
// []
// [-4 -2 0]
// [1 2.1 3.2]
}
```

View File

@@ -34,6 +34,8 @@ import (
- [RoundToFloat](#RoundToFloat) - [RoundToFloat](#RoundToFloat)
- [RoundToString](#RoundToString) - [RoundToString](#RoundToString)
- [TruncRound](#TruncRound) - [TruncRound](#TruncRound)
- [Range](#Range)
- [RangeWithStep](#RangeWithStep)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -476,3 +478,81 @@ func main() {
// 0.125 // 0.125
} }
``` ```
### <span id="Range">Range</span>
<p>根据指定的起始值和数量,创建一个数字切片。</p>
<b>函数签名:</b>
```go
func Range[T constraints.Integer | constraints.Float](start T, count int) []T
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Range(1, 4)
result2 := mathutil.Range(1, -4)
result3 := mathutil.Range(-4, 4)
result4 := mathutil.Range(1.0, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// [1 2 3 4]
// [1 2 3 4]
// [-4 -3 -2 -1]
// [1 2 3 4]
}
```
### <span id="RangeWithStep">RangeWithStep</span>
<p>根据指定的起始值,结束值,步长,创建一个数字切片。</p>
<b>函数签名:</b>
```go
func RangeWithStep[T constraints.Integer | constraints.Float](start, end, step T) []T
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.RangeWithStep(1, 4, 1)
result2 := mathutil.RangeWithStep(1, -1, 0)
result3 := mathutil.RangeWithStep(-4, 1, 2)
result4 := mathutil.RangeWithStep(1.0, 4.0, 1.1)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// [1 2 3]
// []
// [-4 -2 0]
// [1 2.1 3.2]
}
```

View File

@@ -591,20 +591,28 @@ import (
func main() { func main() {
type TodoQuery struct { type TodoQuery struct {
Id int `json:"id"` Id int `json:"id"`
Name string `json:"name"` UserId int `json:"userId"`
Name string `json:"name,omitempty"`
Status string
} }
todoQuery := TodoQuery{ item := TodoQuery{
Id: 1, Id: 1,
Name: "Test", UserId: 123,
Name: "test",
Status: "completed",
} }
todoValues := netutil.StructToUrlValues(todoQuery) queryValues := netutil.StructToUrlValues(item)
fmt.Println(todoValues.Get("id")) fmt.Println(todoValues.Get("id"))
fmt.Println(todoValues.Get("userId"))
fmt.Println(todoValues.Get("name")) fmt.Println(todoValues.Get("name"))
fmt.Println(todoValues.Get("status"))
// Output: // Output:
// 1 // 1
// Test // 123
// test
//
} }
``` ```

View File

@@ -593,20 +593,28 @@ import (
func main() { func main() {
type TodoQuery struct { type TodoQuery struct {
Id int `json:"id"` Id int `json:"id"`
Name string `json:"name"` UserId int `json:"userId"`
Name string `json:"name,omitempty"`
Status string
} }
todoQuery := TodoQuery{ item := TodoQuery{
Id: 1, Id: 1,
Name: "Test", UserId: 123,
Name: "test",
Status: "completed",
} }
todoValues := netutil.StructToUrlValues(todoQuery) queryValues := netutil.StructToUrlValues(item)
fmt.Println(todoValues.Get("id")) fmt.Println(todoValues.Get("id"))
fmt.Println(todoValues.Get("userId"))
fmt.Println(todoValues.Get("name")) fmt.Println(todoValues.Get("name"))
fmt.Println(todoValues.Get("status"))
// Output: // Output:
// 1 // 1
// Test // 123
// test
//
} }
``` ```

View File

@@ -39,7 +39,7 @@ func Values[K comparable, V any](m map[K]V) []V {
} }
// KeysBy creates a slice whose element is the result of function mapper invoked by every map's key. // KeysBy creates a slice whose element is the result of function mapper invoked by every map's key.
// Play: todo // Play: https://go.dev/play/p/hI371iB8Up8
func KeysBy[K comparable, V any, T any](m map[K]V, mapper func(item K) T) []T { func KeysBy[K comparable, V any, T any](m map[K]V, mapper func(item K) T) []T {
keys := make([]T, 0, len(m)) keys := make([]T, 0, len(m))
@@ -51,7 +51,7 @@ func KeysBy[K comparable, V any, T any](m map[K]V, mapper func(item K) T) []T {
} }
// ValuesBy creates a slice whose element is the result of function mapper invoked by every map's value. // ValuesBy creates a slice whose element is the result of function mapper invoked by every map's value.
// Play: todo // Play: https://go.dev/play/p/sg9-oRidh8f
func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T { func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T {
keys := make([]T, 0, len(m)) keys := make([]T, 0, len(m))
@@ -98,7 +98,7 @@ func Filter[K comparable, V any](m map[K]V, predicate func(key K, value V) bool)
} }
// FilterByKeys iterates over map, return a new map whose keys are all given keys. // FilterByKeys iterates over map, return a new map whose keys are all given keys.
// Play: todo // Play: https://go.dev/play/p/7ov6BJHbVqh
func FilterByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V { func FilterByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V {
result := make(map[K]V) result := make(map[K]V)
@@ -111,7 +111,7 @@ func FilterByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V {
} }
// FilterByValues iterates over map, return a new map whose values are all given values. // FilterByValues iterates over map, return a new map whose values are all given values.
// Play: todo // Play: https://go.dev/play/p/P3-9MdcXegR
func FilterByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V { func FilterByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V {
result := make(map[K]V) result := make(map[K]V)
@@ -124,7 +124,7 @@ func FilterByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V {
} }
// OmitBy is the opposite of Filter, removes all the map elements for which the predicate function returns true. // OmitBy is the opposite of Filter, removes all the map elements for which the predicate function returns true.
// Play: todo // Play: https://go.dev/play/p/YJM4Hj5hNwm
func OmitBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool) map[K]V { 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) result := make(map[K]V)
@@ -137,7 +137,7 @@ func OmitBy[K comparable, V any](m map[K]V, predicate func(key K, value V) bool)
} }
// OmitByKeys the opposite of FilterByKeys, extracts all the map elements which keys are not omitted. // OmitByKeys the opposite of FilterByKeys, extracts all the map elements which keys are not omitted.
// Play: todo // Play: https://go.dev/play/p/jXGrWDBfSRp
func OmitByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V { func OmitByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V {
result := make(map[K]V) result := make(map[K]V)
@@ -150,7 +150,7 @@ func OmitByKeys[K comparable, V any](m map[K]V, keys []K) map[K]V {
} }
// OmitByValues the opposite of FilterByValues. remov all elements whose value are in the give slice. // OmitByValues the opposite of FilterByValues. remov all elements whose value are in the give slice.
// Play: todo // Play: https://go.dev/play/p/XB7Y10uw20_U
func OmitByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V { func OmitByValues[K comparable, V comparable](m map[K]V, values []V) map[K]V {
result := make(map[K]V) result := make(map[K]V)
@@ -227,7 +227,7 @@ type Entry[K comparable, V any] struct {
} }
// Entries transforms a map into array of key/value pairs. // Entries transforms a map into array of key/value pairs.
// Play: todo // Play: https://go.dev/play/p/Ltb11LNcElY
func Entries[K comparable, V any](m map[K]V) []Entry[K, V] { func Entries[K comparable, V any](m map[K]V) []Entry[K, V] {
entries := make([]Entry[K, V], 0, len(m)) entries := make([]Entry[K, V], 0, len(m))
@@ -242,7 +242,7 @@ func Entries[K comparable, V any](m map[K]V) []Entry[K, V] {
} }
// FromEntries creates a map based on a slice of key/value pairs // FromEntries creates a map based on a slice of key/value pairs
// Play: todo // Play: https://go.dev/play/p/fTdu4sCNjQO
func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V { func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V {
result := make(map[K]V, len(entries)) result := make(map[K]V, len(entries))
@@ -254,7 +254,7 @@ func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V {
} }
// Transform a map to another type map. // Transform a map to another type map.
// Play: todo // Play: https://go.dev/play/p/P6ovfToM3zj
func Transform[K1 comparable, V1 any, K2 comparable, V2 any](m map[K1]V1, iteratee func(key K1, value V1) (K2, V2)) map[K2]V2 { 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)) result := make(map[K2]V2, len(m))
@@ -267,7 +267,7 @@ func Transform[K1 comparable, V1 any, K2 comparable, V2 any](m map[K1]V1, iterat
} }
// MapKeys transforms a map to other type map by manipulating it's keys. // MapKeys transforms a map to other type map by manipulating it's keys.
// Play: todo // Play: https://go.dev/play/p/8scDxWeBDKd
func MapKeys[K comparable, V any, T comparable](m map[K]V, iteratee func(key K, value V) T) map[T]V { 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)) result := make(map[T]V, len(m))
@@ -279,7 +279,7 @@ func MapKeys[K comparable, V any, T comparable](m map[K]V, iteratee func(key K,
} }
// MapValues transforms a map to other type map by manipulating it's values. // MapValues transforms a map to other type map by manipulating it's values.
// Play: todo // Play: https://go.dev/play/p/g92aY3fc7Iw
func MapValues[K comparable, V any, T any](m map[K]V, iteratee func(key K, value V) T) map[K]T { 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)) result := make(map[K]T, len(m))

View File

@@ -183,3 +183,36 @@ func Average[T constraints.Integer | constraints.Float](numbers ...T) T {
} }
return sum / n return sum / n
} }
// Range creates a slice of numbers from start with specified count, element step is 1.
// Play: todo
func Range[T constraints.Integer | constraints.Float](start T, count int) []T {
size := count
if count < 0 {
size = -count
}
result := make([]T, size)
for i, j := 0, start; i < size; i, j = i+1, j+1 {
result[i] = j
}
return result
}
// RangeWithStep creates a slice of numbers from start to end with specified step.
// Play: todo
func RangeWithStep[T constraints.Integer | constraints.Float](start, end, step T) []T {
result := []T{}
if start >= end || step == 0 {
return result
}
for i := start; i < end; i += step {
result = append(result, i)
}
return result
}

View File

@@ -185,3 +185,39 @@ func ExampleMinBy() {
// ab // ab
// //
} }
func ExampleRange() {
result1 := Range(1, 4)
result2 := Range(1, -4)
result3 := Range(-4, 4)
result4 := Range(1.0, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// [1 2 3 4]
// [1 2 3 4]
// [-4 -3 -2 -1]
// [1 2 3 4]
}
func ExampleRangeWithStep() {
result1 := RangeWithStep(1, 4, 1)
result2 := RangeWithStep(1, -1, 0)
result3 := RangeWithStep(-4, 1, 2)
result4 := RangeWithStep(1.0, 4.0, 1.1)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// [1 2 3]
// []
// [-4 -2 0]
// [1 2.1 3.2]
}

View File

@@ -137,3 +137,33 @@ func TestMinBy(t *testing.T) {
}) })
assert.Equal("", res3) assert.Equal("", res3)
} }
func TestRange(t *testing.T) {
assert := internal.NewAssert(t, "Range")
result1 := Range(1, 4)
result2 := Range(1, -4)
result3 := Range(-4, 4)
result4 := Range(1.0, 4)
result5 := Range(1, 0)
assert.Equal([]int{1, 2, 3, 4}, result1)
assert.Equal([]int{1, 2, 3, 4}, result2)
assert.Equal([]int{-4, -3, -2, -1}, result3)
assert.Equal([]float64{1.0, 2.0, 3.0, 4.0}, result4)
assert.Equal([]int{}, result5)
}
func TestRangeWithStep(t *testing.T) {
assert := internal.NewAssert(t, "Range")
result1 := RangeWithStep(1, 4, 1)
result2 := RangeWithStep(1, -1, 0)
result3 := RangeWithStep(-4, 1, 2)
result4 := RangeWithStep(1.0, 4.0, 1.1)
assert.Equal([]int{1, 2, 3}, result1)
assert.Equal([]int{}, result2)
assert.Equal([]int{-4, -2, 0}, result3)
assert.Equal([]float64{1.0, 2.1, 3.2}, result4)
}

View File

@@ -21,12 +21,11 @@ import (
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"reflect"
"regexp"
"sort" "sort"
"strings" "strings"
"time" "time"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/slice" "github.com/duke-git/lancet/v2/slice"
) )
@@ -219,7 +218,7 @@ func (client *HttpClient) setTLS(rawUrl string) {
} }
} }
// setHeader set http rquest header // setHeader set http request header
func (client *HttpClient) setHeader(req *http.Request, headers http.Header) { func (client *HttpClient) setHeader(req *http.Request, headers http.Header) {
if headers == nil { if headers == nil {
headers = make(http.Header) headers = make(http.Header)
@@ -278,29 +277,15 @@ func validateRequest(req *HttpRequest) error {
// StructToUrlValues convert struct to url valuse, // StructToUrlValues convert struct to url valuse,
// only convert the field which is exported and has `json` tag. // only convert the field which is exported and has `json` tag.
// Play: https://go.dev/play/p/pFqMkM40w9z // Play: https://go.dev/play/p/pFqMkM40w9z
func StructToUrlValues(targetStruct any) url.Values { func StructToUrlValues(targetStruct any) (url.Values, error) {
rv := reflect.ValueOf(targetStruct)
rt := reflect.TypeOf(targetStruct)
if rt.Kind() == reflect.Ptr {
rt = rt.Elem()
}
if rt.Kind() != reflect.Struct {
panic(fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", targetStruct))
}
result := url.Values{} result := url.Values{}
s, err := convertor.StructToMap(targetStruct)
fieldNum := rt.NumField() if err != nil {
pattern := `^[A-Z]` return nil, err
regex := regexp.MustCompile(pattern)
for i := 0; i < fieldNum; i++ {
name := rt.Field(i).Name
tag := rt.Field(i).Tag.Get("json")
if regex.MatchString(name) && tag != "" {
result.Add(tag, fmt.Sprintf("%v", rv.Field(i).Interface()))
} }
for k, v := range s {
result.Add(k, fmt.Sprintf("%v", v))
} }
return result return result, nil
} }

View File

@@ -219,28 +219,29 @@ func TestStructToUrlValues(t *testing.T) {
type TodoQuery struct { type TodoQuery struct {
Id int `json:"id"` Id int `json:"id"`
UserId int `json:"userId"` UserId int `json:"userId"`
Name string `json:"name,omitempty"`
} }
todoQuery := TodoQuery{ item1 := TodoQuery{
Id: 1, Id: 1,
UserId: 1, UserId: 123,
Name: "",
}
todoValues, err := StructToUrlValues(item1)
if err != nil {
t.Errorf("params is invalid: %v", err)
} }
todoValues := StructToUrlValues(todoQuery)
assert.Equal("1", todoValues.Get("id")) assert.Equal("1", todoValues.Get("id"))
assert.Equal("1", todoValues.Get("userId")) assert.Equal("123", todoValues.Get("userId"))
assert.Equal("", todoValues.Get("name"))
request := &HttpRequest{ item2 := TodoQuery{
RawURL: "https://jsonplaceholder.typicode.com/todos", Id: 2,
Method: "GET", UserId: 456,
QueryParams: todoValues,
} }
queryValues2, _ := StructToUrlValues(item2)
httpClient := NewHttpClient() assert.Equal("2", queryValues2.Get("id"))
resp, err := httpClient.SendRequest(request) assert.Equal("456", queryValues2.Get("userId"))
if err != nil || resp.StatusCode != 200 { assert.Equal("", queryValues2.Get("name"))
log.Fatal(err)
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", string(body))
} }

View File

@@ -124,20 +124,44 @@ func ExampleHttpClient_DecodeResponse() {
func ExampleStructToUrlValues() { func ExampleStructToUrlValues() {
type TodoQuery struct { type TodoQuery struct {
Id int `json:"id"` Id int `json:"id"`
Name string `json:"name"` UserId int `json:"userId"`
Name string `json:"name,omitempty"`
Status string
} }
todoQuery := TodoQuery{ item1 := TodoQuery{
Id: 1, Id: 1,
Name: "Test", UserId: 123,
Name: "test",
Status: "completed",
}
queryValues1, err := StructToUrlValues(item1)
if err != nil {
return
} }
todoValues := StructToUrlValues(todoQuery)
fmt.Println(todoValues.Get("id")) item2 := TodoQuery{
fmt.Println(todoValues.Get("name")) Id: 2,
UserId: 456,
}
queryValues2, _ := StructToUrlValues(item2)
fmt.Println(queryValues1.Get("id"))
fmt.Println(queryValues1.Get("userId"))
fmt.Println(queryValues1.Get("name"))
fmt.Println(queryValues1.Get("status"))
fmt.Println(queryValues2.Get("id"))
fmt.Println(queryValues2.Get("userId"))
fmt.Println(queryValues2.Get("name"))
// Output: // Output:
// 1 // 1
// Test // 123
// test
//
// 2
// 456
//
} }
func ExampleConvertMapToQueryString() { func ExampleConvertMapToQueryString() {

View File

@@ -22,7 +22,7 @@ var (
) )
// Contain check if the target value is in the slice or not. // Contain check if the target value is in the slice or not.
// Play: todo // Play: https://go.dev/play/p/_454yEHcNjf
func Contain[T comparable](slice []T, target T) bool { func Contain[T comparable](slice []T, target T) bool {
for _, item := range slice { for _, item := range slice {
if item == target { if item == target {
@@ -34,6 +34,7 @@ func Contain[T comparable](slice []T, target T) bool {
} }
// ContainBy returns true if predicate function return true. // ContainBy returns true if predicate function return true.
// Play: https://go.dev/play/p/49tkHfX4GNc
func ContainBy[T any](slice []T, predicate func(item T) bool) bool { func ContainBy[T any](slice []T, predicate func(item T) bool) bool {
for _, item := range slice { for _, item := range slice {
if predicate(item) { if predicate(item) {
@@ -436,7 +437,7 @@ func Map[T any, U any](slice []T, iteratee func(index int, item T) U) []U {
// iteratee callback function should returntwo values: // iteratee callback function should returntwo values:
// 1, mapping result. // 1, mapping result.
// 2, whether the result element should be included or not // 2, whether the result element should be included or not
// Play: todo // Play: https://go.dev/play/p/J94SZ_9MiIe
func FilterMap[T any, U any](slice []T, iteratee func(index int, item T) (U, bool)) []U { func FilterMap[T any, U any](slice []T, iteratee func(index int, item T) (U, bool)) []U {
result := []U{} result := []U{}
@@ -450,7 +451,7 @@ func FilterMap[T any, U any](slice []T, iteratee func(index int, item T) (U, boo
} }
// FlatMap manipulates a slice and transforms and flattens it to a slice of another type. // FlatMap manipulates a slice and transforms and flattens it to a slice of another type.
// Play: todo // Play: https://go.dev/play/p/_QARWlWs1N_F
func FlatMap[T any, U any](slice []T, iteratee func(index int, item T) []U) []U { func FlatMap[T any, U any](slice []T, iteratee func(index int, item T) []U) []U {
result := make([]U, 0, len(slice)) result := make([]U, 0, len(slice))

View File

@@ -64,6 +64,7 @@ func Generate[T any](generator func() func() (item T, ok bool)) stream[T] {
var zeroValue T var zeroValue T
for next, item, ok := generator(), zeroValue, true; ok; { for next, item, ok := generator(), zeroValue, true; ok; {
item, ok = next() item, ok = next()
if ok { if ok {
source = append(source, item) source = append(source, item)
@@ -98,7 +99,7 @@ func FromRange[T constraints.Integer | constraints.Float](start, end, step T) st
} }
l := int((end-start)/step) + 1 l := int((end-start)/step) + 1
source := make([]T, l, l) source := make([]T, l)
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
source[i] = start + (T(i) * step) source[i] = start + (T(i) * step)
@@ -160,7 +161,7 @@ func (s stream[T]) Filter(predicate func(item T) bool) stream[T] {
// Map returns a stream consisting of the elements of this stream that apply the given function to elements of stream. // Map returns a stream consisting of the elements of this stream that apply the given function to elements of stream.
func (s stream[T]) Map(mapper func(item T) T) stream[T] { func (s stream[T]) Map(mapper func(item T) T) stream[T] {
source := make([]T, s.Count(), s.Count()) source := make([]T, s.Count())
for i, v := range s.source { for i, v := range s.source {
source[i] = mapper(v) source[i] = mapper(v)

55
structutil/field.go Normal file
View File

@@ -0,0 +1,55 @@
package structutil
import "reflect"
type Field struct {
value reflect.Value
field reflect.StructField
tag *Tag
}
func newField(v reflect.Value, f reflect.StructField, tagName string) *Field {
tag := f.Tag.Get(tagName)
return &Field{
value: v,
field: f,
tag: newTag(tag),
}
}
// Tag returns the value that the key in the tag string.
func (f *Field) Tag() *Tag {
return f.tag
}
// Value returns the underlying value of the field.
func (f *Field) Value() any {
return f.value.Interface()
}
// IsEmbedded returns true if the given field is an embedded field.
func (f *Field) IsEmbedded() bool {
return f.field.Anonymous
}
// IsExported returns true if the given field is exported.
func (f *Field) IsExported() bool {
return f.field.PkgPath == ""
}
// IsZero returns true if the given field is zero value.
func (f *Field) IsZero() bool {
z := reflect.Zero(f.value.Type()).Interface()
v := f.Value()
return reflect.DeepEqual(z, v)
}
// Name returns the name of the given field
func (f *Field) Name() string {
return f.field.Name
}
// Kind returns the field's kind
func (f *Field) Kind() reflect.Kind {
return f.value.Kind()
}

88
structutil/struct.go Normal file
View File

@@ -0,0 +1,88 @@
package structutil
import (
"reflect"
)
// DefaultTagName is the default tag for struct fields to lookup.
var DefaultTagName = "json"
// Struct is abstract struct for provide several high level functions
type Struct struct {
raw any
rtype reflect.Type
rvalue reflect.Value
TagName string
}
// New returns a new *Struct
func New(value any) *Struct {
v := reflect.ValueOf(value)
t := reflect.TypeOf(value)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
return &Struct{
raw: value,
rtype: t,
rvalue: v,
TagName: DefaultTagName,
}
}
// ToMap converts the given struct to a map[string]any, where the keys
// of the keys are the field names and the values of the map are the values
// of the fields. The default map key is the struct field name, but you can
// change it. The `json` key is the default tag key. Example:
//
// // default
// Name string `json:"name"`
//
// // ignore the field
// Name string // no tag
// Age string `json:"-"` // json ignore tag
// sex string // unexported field
// Goal int `json:"goal,omitempty"` // omitempty if the field is zero value
//
// // custom map key
// Name string `json:"myName"`
//
// Only the exported fields of a struct can be converted.
func (s *Struct) ToMap() (map[string]any, error) {
result := make(map[string]any)
fields := s.Fields()
for _, f := range fields {
if !f.IsExported() || f.tag.IsEmpty() || f.tag.Name == "-" {
continue
}
if f.IsZero() && f.tag.HasOption("omitempty") {
continue
}
// TODO: sub struct
result[f.tag.Name] = f.Value()
}
return result, nil
}
// Fields returns all the struct fields within a slice
func (s *Struct) Fields() []*Field {
var fields []*Field
fieldNum := s.rvalue.NumField()
for i := 0; i < fieldNum; i++ {
v := s.rvalue.Field(i)
sf := s.rtype.Field(i)
field := newField(v, sf, DefaultTagName)
fields = append(fields, field)
}
return fields
}
// ToMap convert struct to map, only convert exported struct field
// map key is specified same as struct field tag `json` value.
func ToMap(v any) (map[string]any, error) {
return New(v).ToMap()
}

57
structutil/struct_test.go Normal file
View File

@@ -0,0 +1,57 @@
package structutil
import (
"github.com/duke-git/lancet/v2/internal"
"testing"
)
func TestToMap(t *testing.T) {
assert := internal.NewAssert(t, "TestStructToMap")
t.Run("StructToMap", func(_ *testing.T) {
type People struct {
Name string `json:"name"`
age int
}
p := People{
"test",
100,
}
pm, _ := ToMap(p)
var expected = map[string]any{"name": "test"}
assert.Equal(expected, pm)
})
t.Run("StructToMapWithJsonAttr", func(_ *testing.T) {
type People struct {
Name string `json:"name,omitempty"` // json tag with attribute
Phone string `json:"phone"` // json tag without attribute
Sex string `json:"-"` // ignore by "-"
Age int // ignore by no tag
email string // ignore by unexported
IsWorking bool `json:"is_working"`
}
p1 := People{
Name: "AAA", // exist
Phone: "1111",
Sex: "male",
Age: 100,
email: "11@gmail.com",
}
p1m, _ := ToMap(p1)
var expect1 = map[string]any{"name": "AAA", "phone": "1111", "is_working": false}
assert.Equal(expect1, p1m)
p2 := People{
Name: "",
Phone: "2222",
Sex: "male",
Age: 0,
email: "22@gmail.com",
IsWorking: true,
}
p2m, _ := ToMap(p2)
var expect2 = map[string]any{"phone": "2222", "is_working": true}
assert.Equal(expect2, p2m)
})
}

32
structutil/tag.go Normal file
View File

@@ -0,0 +1,32 @@
package structutil
import (
"github.com/duke-git/lancet/v2/validator"
"strings"
)
type Tag struct {
Name string
Options []string
}
func newTag(tag string) *Tag {
res := strings.Split(tag, ",")
return &Tag{
Name: res[0],
Options: res[1:],
}
}
func (t *Tag) HasOption(opt string) bool {
for _, o := range t.Options {
if o == opt {
return true
}
}
return false
}
func (t *Tag) IsEmpty() bool {
return validator.IsEmptyString(t.Name)
}

View File

@@ -69,7 +69,7 @@ func LowerFirst(s string) string {
// PadStart pads string on the left and 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. // Padding characters are truncated if they exceed size.
// Play: todo // Play: https://go.dev/play/p/NzImQq-VF8q
func Pad(source string, size int, padStr string) string { func Pad(source string, size int, padStr string) string {
return padAtPosition(source, size, padStr, 0) return padAtPosition(source, size, padStr, 0)
} }
@@ -289,7 +289,7 @@ func Substring(s string, offset int, length uint) string {
} }
// SplitWords splits a string into words, word only contains alphabetic characters. // SplitWords splits a string into words, word only contains alphabetic characters.
// Play: todo // Play: https://go.dev/play/p/KLiX4WiysMM
func SplitWords(s string) []string { func SplitWords(s string) []string {
var word string var word string
var words []string var words []string
@@ -331,7 +331,7 @@ func SplitWords(s string) []string {
} }
// WordCount return the number of meaningful word, word only contains alphabetic characters. // WordCount return the number of meaningful word, word only contains alphabetic characters.
// Play: todo // Play: https://go.dev/play/p/bj7_odx3vRf
func WordCount(s string) int { func WordCount(s string) int {
var r rune var r rune
var size, count int var size, count int