1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-09 15:12:26 +08:00

Compare commits

...

31 Commits

Author SHA1 Message Date
dudaodong
66efe61834 release v2.1.16 2023-03-01 10:08:53 +08:00
dudaodong
94f7f3e0e5 doc: add new function docs 2023-02-28 15:03:02 +08:00
dudaodong
9d2c9806d1 feat: add ContainBy function 2023-02-24 14:19:10 +08:00
dudaodong
e523d4af6e doc: add doc for FilterMap and FlatMap 2023-02-24 11:23:02 +08:00
dudaodong
f3801bcd8f feat: add FlatMap 2023-02-24 11:14:59 +08:00
dudaodong
5767aad303 feat: add FilterMap 2023-02-24 10:55:27 +08:00
dudaodong
f7aaa1ed2a fix: fix issue #75 2023-02-23 11:43:41 +08:00
dudaodong
ef19a414bc doc: fix doc error 2023-02-23 10:58:43 +08:00
dudaodong
65704dea06 doc: fix doc error 2023-02-23 10:57:48 +08:00
dudaodong
5eac358bb0 doc: format code in doc file 2023-02-22 17:00:46 +08:00
dudaodong
8e36ef5cef doc: add doc for new map functions 2023-02-22 16:59:01 +08:00
dudaodong
21b0d2ec0e doc: add doc for WordCount and SplitWords function 2023-02-22 16:13:41 +08:00
dudaodong
32ca975204 feat: add OmitBy, OmitByKeys, OmitByValues 2023-02-21 14:52:10 +08:00
dudaodong
ec740e442c feat: add WordCount 2023-02-21 14:27:00 +08:00
dudaodong
ed98ad53ec feat: add SplitWords 2023-02-21 14:16:36 +08:00
dudaodong
af7b9d2710 doc: add doc for CopyProperties function 2023-02-20 14:10:55 +08:00
dudaodong
f08b368001 fix: update param type of CopyProperties 2023-02-20 14:04:45 +08:00
dudaodong
be8a0558f8 test: add examples for CopyProperties function 2023-02-20 12:00:49 +08:00
dudaodong
46de539e22 feat: add CopyProperties for merge properties between structs 2023-02-20 11:48:30 +08:00
dudaodong
36fb3abe9e test: add examples for maputil package function 2023-02-19 16:59:21 +08:00
dudaodong
fee8d325b7 doc: add doc for Pad function 2023-02-19 12:58:14 +08:00
dudaodong
8784be1583 feat: add Pad function 2023-02-19 12:37:26 +08:00
dudaodong
84cd68de7f fix: fix bugs of PadEnd and PadStart 2023-02-19 12:34:39 +08:00
dudaodong
a774c060ce feat: add MapKeys and MapValues 2023-02-17 15:41:58 +08:00
dudaodong
730f061b95 feat: add Entries FromEntries Transform 2023-02-17 15:24:46 +08:00
dudaodong
2eb08f3404 feat: add FilterByValues function 2023-02-17 11:59:44 +08:00
dudaodong
4c1496b648 feat: add FilterByKeys function 2023-02-17 11:57:09 +08:00
dudaodong
c2ae784f27 feat: add ValuesBy function 2023-02-17 11:48:11 +08:00
dudaodong
e71cecefea feat: add KeysBy function 2023-02-17 11:40:20 +08:00
dudaodong
572e53aa14 doc: add go playground demo for xerror package 2023-02-16 14:28:50 +08:00
dudaodong
ca9ecb9c8a update go playground demo 2023-02-16 14:10:24 +08:00
28 changed files with 3073 additions and 137 deletions

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.1.15-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.1.16-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](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

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.1.15-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.1.16-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](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 加密包支持数据加密和解密,获取 md5hash 值。支持 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)]
## 如何贡献代码

View File

@@ -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
}

View File

@@ -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}
}

View File

@@ -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)
}

View File

@@ -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}
}
```

View File

@@ -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}
}
```

View File

@@ -29,6 +29,7 @@ import (
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Delay](#Delay)
- [Schedule](#Schedule)
- [Pipeline](#Pipeline)
- [Watcher](#Watcher)

View File

@@ -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++
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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)
}

View File

@@ -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>

View File

@@ -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
}
```

View File

@@ -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
}
```

View File

@@ -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:

View File

@@ -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:

View File

@@ -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
}

View File

@@ -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]
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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}

View File

@@ -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{
{},

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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))
}
}