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

Compare commits

...

102 Commits

Author SHA1 Message Date
dudaodong
1eb793420e doc: add Gurubase and update Sponsor text 2024-11-19 10:29:41 +08:00
dudaodong
05f9854945 Merge branch 'rc' 2024-11-19 10:26:41 +08:00
dudaodong
3be706e23f doc: add doc for Permutation and Combination function 2024-11-19 10:25:32 +08:00
Kursat Aktas
b5e7312353 Introducing Lancet Guru on Gurubase.io (#271) 2024-11-19 10:21:30 +08:00
dudaodong
95a894e53f feat: add Permutation and Combination 2024-11-19 10:12:07 +08:00
dudaodong
8322951475 feat: add StdDev and fix bug of Average for math package 2024-11-18 17:01:27 +08:00
dudaodong
6b2c91b0f6 feat: add Variance for math package 2024-11-18 16:25:32 +08:00
dudaodong
ec161f335d fix: update random seed when call random function 2024-11-14 16:27:22 +08:00
feng6917
d1a8f37a71 Correction of word assert spelling mistakes (#269) 2024-11-12 19:15:23 +08:00
dudaodong
a769257017 fix: update random seed when call random function 2024-11-11 19:45:31 +08:00
dudaodong
f3579fc142 feat: add IndexOf and LastIndexOf for stream 2024-11-11 10:52:29 +08:00
dudaodong
de877e5278 feat: add Ternary 2024-11-08 14:20:35 +08:00
dudaodong
08f14d2b08 feat: add ExtractContent
:
2024-11-08 14:11:25 +08:00
dudaodong
0ed2b11ba1 doc: add doc for Min, Max, MaxMin function 2024-11-07 16:00:34 +08:00
dudaodong
643edc1468 feat: add Max, Min, and MaxMin for datetime package 2024-11-07 15:47:22 +08:00
dudaodong
e2ff83649a doc: format code in doc file 2024-11-06 10:15:47 +08:00
残念
a7fecfc73b perf(slice): make the IndexOf function thread-safe (#263) 2024-11-06 10:04:44 +08:00
dudaodong
8bbae69175 feat: add try-catch-finally implementation 2024-11-04 14:46:25 +08:00
dudaodong
840ea8f3c5 fix: fix code in UnZip demo 2024-10-25 11:14:15 +08:00
dudaodong
a4e89bd7c1 feat: add ConcatBy 2024-10-24 15:38:12 +08:00
dudaodong
2015d36b08 feat: add JoinFunc 2024-10-24 14:56:13 +08:00
dudaodong
921f218ef7 test: run test cases on rc branch 2024-10-18 16:40:20 +08:00
dudaodong
0a2cc9c928 doc: update doc for RandNumberOfLength and GetExeOrDllVersion 2024-10-18 16:38:39 +08:00
今晚打打
ed93aae970 feat:add GetExeDllVersion for fileutil,random,package (#257)
* 增加GetExeDllVersion函数获取exe,dll版本号

* 多打文字

* 定位忘记...

* 单独剥离出来一个为windows的,增加RandNumLen为取指定长度随机数.

* ....

* 增加test测试
2024-10-18 16:17:14 +08:00
BoWen Qiu
1671f7856a feat(netutil): add async http request for http client (#256)
* fix(algorithm): fix bug of lrucache in issue #251

* feat(netutil): add async send request for HttpClient
2024-10-18 15:14:52 +08:00
燕归来
1008dd4956 fix: fix compile error (#255) (#258) 2024-10-18 15:09:28 +08:00
BoWen Qiu
a254ebdc8e fix(algorithm): fix bug of lrucache in issue #251 (#254) 2024-10-16 09:55:38 +08:00
dudaodong
0bc11001a4 fix: add rsa public and private key example file 2024-10-12 15:16:39 +08:00
dudaodong
85d98ad915 Merge branch 'rc' into v2 2024-10-12 14:06:15 +08:00
77
cb08613ac3 fix(orderedmap): fix set bug (#252)
set : when the key is present, the value of the data map should be set,
not the value of the list

Co-authored-by: congziqi <congziqi@lixiang.com>
2024-10-12 14:05:13 +08:00
dudaodong
bad1b05224 doc: add doc for RsaSign and RsaVerifySign 2024-10-11 15:38:42 +08:00
dudaodong
527328739a feat: add RsaSign and RsaVerifySign 2024-10-10 15:40:32 +08:00
dudaodong
213e2b4ead doc: add play ground demo 2024-10-09 16:56:56 +08:00
dudaodong
5d6ab72059 release v2.3.3 2024-10-09 15:20:30 +08:00
dudaodong
30eb2c72b0 doc: rename parameter name of Comma 2024-09-27 10:23:29 +08:00
dudaodong
adf18a2e47 fix: fix bug of Comma, issue #248 2024-09-27 10:18:37 +08:00
dudaodong
f99a8ef3cf refactoring: defect #247 2024-09-20 19:29:31 +08:00
Emmanuel Ferdman
fcdf1d5839 doc: update contribution references (#246)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2024-09-19 09:03:50 +08:00
dudaodong
ee0afed963 Merge branch 'v2' into rc 2024-09-16 09:10:23 +08:00
Kyden
1d94896c9b fix(strutil): rename PadStart to Pad. (#245) 2024-09-15 20:29:12 +08:00
dudaodong
69cf9bbcf0 doc: add new functions in system package 2024-09-13 16:21:57 +08:00
dudaodong
2bbcb85286 feat: add GetProcessInfo for system package 2024-09-13 16:10:09 +08:00
dudaodong
e58c9b797b feat: add StartProcess, StopProcess, KillProcess for system package 2024-09-13 15:09:44 +08:00
dudaodong
3e1ac5e0b5 test: remove unstable test item 2024-09-10 15:59:09 +08:00
dudaodong
8869e0440d doc: update doc for GetOrDefault 2024-09-10 15:54:54 +08:00
yunxuan
69f9c74bcb feat(maputil): add GetValue (#243)
* add maputil.GetValue

* rename GetOrDefault
2024-09-10 15:08:36 +08:00
dudaodong
84ebc7ce71 doc: add doc for new functions of release v2.3.3 2024-09-10 14:47:38 +08:00
dudaodong
c745097749 feat: add AesGcmEncrypt and AesGcmDecrypt in cryptor package 2024-09-10 10:37:47 +08:00
dudaodong
ba75e58e5f feat: add datetime package doc 2024-09-09 14:42:01 +08:00
dudaodong
7e85a0ed7d doc: update doc 2024-09-09 11:51:10 +08:00
dudaodong
2268a0312f doc: update doc 2024-09-09 11:49:53 +08:00
dudaodong
da84d95aa3 feat: add GenerateDatetimesBetween in datetime package 2024-09-09 11:35:17 +08:00
dudaodong
48244d6711 feat: add DaysBetween in datetime package 2024-09-09 10:55:18 +08:00
dudaodong
5e3337a52e feat: add TrackFuncTime in datetime package 2024-09-06 15:51:56 +08:00
dudaodong
c3372e18b1 feat: add Frequency in slice package 2024-09-06 15:06:35 +08:00
dudaodong
90e5a0bfb2 feat: add RegexMatchAllGroups 2024-09-06 14:19:57 +08:00
dudaodong
93be25920f feat: add TemplateReplace 2024-09-06 11:04:54 +08:00
dudaodong
f9e5ec9096 feat: add Rotate for string 2024-09-03 16:36:11 +08:00
dudaodong
601df5dc12 feat: add Rotate for string 2024-09-03 16:35:38 +08:00
dudaodong
63216d9b1c feat: add Shuffle for string 2024-09-03 15:54:05 +08:00
dudaodong
c32a19868d refactoring: make test clear 2024-09-03 15:33:32 +08:00
dudaodong
71e914019b feat: add Ellipsis for string 2024-09-03 14:36:12 +08:00
dudaodong
9824db0056 Merge branch 'rc' 2024-09-02 14:31:59 +08:00
dudaodong
ba9188a29a doc: update check link 2024-09-02 14:28:26 +08:00
dudaodong
8625fbd8d3 doc: update doc for ExecCommand function 2024-09-02 11:22:11 +08:00
dudaodong
81b29baf30 doc: add doc for OrderedMap 2024-09-02 11:19:05 +08:00
dudaodong
5a38e34063 Merge branch 'rc' of github.com:duke-git/lancet into rc 2024-08-30 16:55:10 +08:00
DerekTond
159168dd7b fix 删除文档中废弃的RetryDuration函数 (#240)
* fix 修改文档中已经删除的函数RetryDuration,替换为RetryWithLinearBackoff

* fix 删除主文档中的废弃函数

---------

Co-authored-by: dongyue16 <dongyue16@tal.com>
2024-08-29 19:37:42 +08:00
dudaodong
ec092a009a test: we should write clean unit test code 2024-08-28 16:06:08 +08:00
dudaodong
ca40b5d6c6 refactoring: rename SortByKeys to SortByKey 2024-08-28 10:58:14 +08:00
dudaodong
a6d39a3bba feat: add OrderedMap for issue #237 2024-08-28 10:53:53 +08:00
dudaodong
38148978cf refactoring: change unit variable to package internal 2024-08-26 16:25:57 +08:00
dudaodong
3e8c3bd396 feat: add SortbyKeys for map 2024-08-23 11:21:29 +08:00
dudaodong
30971c1aab doc: rename CONTRIBUTION.md and add github start chart 2024-08-21 10:28:26 +08:00
dudaodong
bc260277bc fix: fix DecimalBytes and BinaryBytes issue #232 2024-08-19 11:36:49 +08:00
dudaodong
c0b200f846 feat: add ReduceConcurrent 2024-08-15 17:48:26 +08:00
dudaodong
305847993c feat: add ForEachConcurrent 2024-08-15 16:44:22 +08:00
dudaodong
f5d70728c3 refactoring: rename param and change its order 2024-08-15 15:50:48 +08:00
残念
c2a5335bc6 feat: add RandSliceFromGivenSlice function (#236) 2024-08-15 15:20:36 +08:00
残念
7b4e060f85 feat: add RandFromGivenSlice function (#235)
* feat: add RandFromGivenSlice function

* feat: add RandFromGivenSlice function
2024-08-14 19:36:12 +08:00
dudaodong
a360372aa9 feat: add FilterConcurrent 2024-08-14 11:19:10 +08:00
dudaodong
7f78a6b11e refactoring: rename slice_parallel to slice_concurrent 2024-08-14 10:52:36 +08:00
dudaodong
5c53cb5867 feat: add MapConcurrent 2024-08-14 10:45:35 +08:00
dudaodong
f7e9d5dc47 doc: add doc for UniqueByComparator and UniqueByParallel 2024-08-13 11:00:47 +08:00
dudaodong
5c580ed013 test: update test for Schedule 2024-08-09 15:00:19 +08:00
dudaodong
0f9764f41e feat: add Throttle function 2024-08-09 14:28:15 +08:00
dudaodong
0bc5f82554 doc: add RandBool, RandBoolSlice 2024-08-09 11:47:36 +08:00
dudaodong
e91965b013 feat: add RandStringSlice, RandBool, RandBoolSlice 2024-08-09 11:44:56 +08:00
dudaodong
483a286d8e feat: add RandIntSlice function 2024-08-09 10:46:08 +08:00
dudaodong
3f8e306ced doc: update deprecated warning text 2024-08-08 15:44:19 +08:00
dudaodong
0b29f0520d feat: add Debounce function 2024-08-08 15:19:38 +08:00
dudaodong
8611ec0c10 feat: add UniqueByComparator 2024-08-08 11:23:11 +08:00
dudaodong
286e10d189 refactoring: memory optimization for unique slice 2024-08-08 10:51:33 +08:00
dudaodong
3e7f94b03e fix: fix UniqueBy bug 2024-08-08 10:40:23 +08:00
dudaodong
356351896d feat: add UniqueByParallel for slice 2024-08-08 10:20:52 +08:00
dudaodong
9be124211e refact: preallocate in Merge map 2024-08-01 11:00:24 +08:00
dudaodong
f467658481 Merge branch 'rc' into v2 2024-07-31 10:50:29 +08:00
zyfx
5c9d0e396e Update condition.go (#234) 2024-07-31 10:16:03 +08:00
dudaodong
8f74460c1b fix: remove scientific notation in DecimalBytes 2024-07-30 14:17:17 +08:00
dudaodong
d5752499bf fix: remove scientific notation in DecimalBytes 2024-07-29 11:42:56 +08:00
dudaodong
eb7cf76eae doc: fix doc error 2024-07-18 11:44:00 +08:00
dudaodong
4af074d181 doc: add play ground demo 2024-07-18 11:34:16 +08:00
107 changed files with 11254 additions and 1179 deletions

2
.gitignore vendored
View File

@@ -8,7 +8,7 @@ fileutil/*.link
fileutil/unzip/*
fileutil/tempdir/*
slice/testdata/*
cryptor/*.pem
# cryptor/*.pem
test
docs/node_modules
docs/.vitepress/cache

View File

@@ -1,4 +1,4 @@
# Lancet Contributing Guide
# Lancet Contribution Guide
Hi! Thank you for choosing Lancet.

192
README.md
View File

@@ -4,12 +4,13 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.3.2-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.3-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)
[![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/duke-git/lancet/blob/main/LICENSE)
[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20Lancet%20Guru-006BFF)](https://gurubase.io/g/lancet)
</div>
@@ -24,7 +25,7 @@
## Features
- 👏 Comprehensive, efficient and reusable.
- 💪 600+ go util functions, support string, slice, datetime, net, crypt...
- 💪 700+ go util functions, support string, slice, datetime, net, crypt...
- 💅 Only depends on two kinds of libraries: go standard library and golang.org/x.
- 🌍 Unit test for every exported function.
@@ -38,7 +39,7 @@
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.4.3. </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.4.4. </b>
```go
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x
@@ -362,6 +363,12 @@ import "github.com/duke-git/lancet/v2/cryptor"
- **<big>AesOfbDecrypt</big>** : decrypt byte slice data with key use AES OFB algorithm.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesOfbDecrypt)]
[[play](https://go.dev/play/p/VtHxtkUj-3F)]
- **<big>AesGcmEncrypt</big>** : encrypt byte slice data with key use AES GCM algorithm.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesGcmEncrypt)]
[[play](https://go.dev/play/p/rUt0-DmsPCs)]
- **<big>AesGcmDecrypt</big>** : decrypt byte slice data with key use AES GCM algorithm.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#AesGcmDecrypt)]
[[play](https://go.dev/play/p/rUt0-DmsPCs)]
- **<big>Base64StdEncode</big>** : encode string with base64 encoding.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#Base64StdEncode)]
[[play](https://go.dev/play/p/VOaUyQUreoK)]
@@ -603,6 +610,16 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>TimestampNano</big>** : returns current nano second timestamp.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TimestampNano)]
[[play](https://go.dev/play/p/A9Oq_COrcCF)]
- **<big>TrackFuncTime</big>** : tracks function execution time.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TrackFuncTime)]
[[play](https://go.dev/play/p/QBSEdfXHPTp)]
- **<big>DaysBetween</big>** : returns the number of days between two times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#DaysBetween)]
[[play](https://go.dev/play/p/qD6qGb3TbOy)]
- **<big>GenerateDatetimesBetween</big>** : returns a slice of strings between two times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#GenerateDatetimesBetween)]
[[play](https://go.dev/play/p/6kHBpAxD9ZC)]
<h3 id="datastructure"> 8. Datastructure package contains some common data structure. eg. list, linklist, stack, queue, set, tree, graph. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -747,7 +764,7 @@ import "github.com/duke-git/lancet/v2/formatter"
#### Function list:
- **<big>Comma</big>** : add comma to a number value by every 3 numbers from right, ahead by symbol char.
- **<big>Comma</big>** : add comma to a number value by every 3 numbers from right, ahead by a prefix symbol char.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/formatter.md#Comma)]
[[play](https://go.dev/play/p/eRD5k2vzUVX)]
- **<big>Pretty</big>** : pretty print data to JSON string.
@@ -792,9 +809,15 @@ import "github.com/duke-git/lancet/v2/function"
- **<big>Delay</big>** : call the function after delayed time.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Delay)]
[[play](https://go.dev/play/p/Ivtc2ZE-Tye)]
- **<big>Debounced</big>** : creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
- **<big>Debounced<sup>deprecated</sup></big>** : creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Debounced)]
[[play](https://go.dev/play/p/absuEGB_GN7)]
- **<big>Debounce</big>** : creates a debounced version of the provided function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Debounce)]
[[play](https://go.dev/play/p/-dGFrYn_1Zi)]
- **<big>Throttle</big>** : creates a throttled version of the provided function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Throttle)]
[[play](https://go.dev/play/p/HpoMov-tJSN)]
- **<big>Schedule</big>** : invoke function every duration time, util close the returned bool channel.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Schedule)]
[[play](https://go.dev/play/p/hbON-Xeyn5N)]
@@ -903,6 +926,7 @@ import "github.com/duke-git/lancet/v2/maputil"
[[play](https://go.dev/play/p/isZZHOsDhFc)]
- **<big>GetOrSet</big>** : returns value of the given key or set the given value value if not present.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#GetOrSet)]
[[play](https://go.dev/play/p/IVQwO1OkEJC)]
- **<big>MapToStruct</big>** : converts map to struct.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapToStruct)]
[[play](https://go.dev/play/p/7wYyVfX38Dp)]
@@ -912,6 +936,60 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ToSortedSlicesWithComparator</big>** : converts a map to two slices sorted by key and using a custom comparison function: one for the keys and another for the values.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesWithComparator)]
[[play](https://go.dev/play/p/0nlPo6YLdt3)]
- **<big>NewOrderedMap</big>** : creates a new OrderedMap.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewOrderedMap)]
[[play](https://go.dev/play/p/Y4ZJ_oOc1FU)]
- **<big>OrderedMap_Set</big>** : sets the given key-value pair for ordered map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Set)]
[[play](https://go.dev/play/p/Y4ZJ_oOc1FU)]
- **<big>OrderedMap_Get</big>** : returns the value for the given key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Get)]
[[play](https://go.dev/play/p/Y4ZJ_oOc1FU)]
- **<big>OrderedMap_Delete</big>** : deletes the key-value pair for the given key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Delete)]
[[play](ttps://go.dev/play/p/5bIi4yaZ3K-)]
- **<big>OrderedMap_Clear</big>** : clears the ordered map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Clear)]
[[play](https://go.dev/play/p/8LwoJyEfuFr)]
- **<big>OrderedMap_Front</big>** : returns the first key-value pair.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Front)]
[[play](https://go.dev/play/p/ty57XSimpoe)]
- **<big>OrderedMap_Back</big>** : returns the last key-value pair.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Back)]
[[play](https://go.dev/play/p/rQMjp1yQmpa)]
- **<big>OrderedMap_Range</big>** : calls the given function for each key-value pair.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Range)]
[[play](https://go.dev/play/p/U-KpORhc7LZ)]
- **<big>OrderedMap_Keys</big>** : returns the keys in order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Keys)]
[[play](https://go.dev/play/p/Vv_y9ExKclA)]
- **<big>OrderedMap_Values</big>** : returns the values in order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Values)]
[[play](https://go.dev/play/p/TWj5n1-PUfx)]
- **<big>OrderedMap_Elements</big>** : returns the key-value pairs in order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Elements)]
[[play](https://go.dev/play/p/4BHG4kKz6bB)]
- **<big>OrderedMap_Len</big>** : returns the number of key-value pairs.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Len)]
[[play](https://go.dev/play/p/cLe6z2VX5N-)]
- **<big>OrderedMap_Contains</big>** : returns true if the given key exists.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Contains)]
[[play](https://go.dev/play/p/QuwqqnzwDNX)]
- **<big>OrderedMap_Iter</big>** : returns a channel that yields key-value pairs in order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_Iter)]
[[play](https://go.dev/play/p/tlq2tdvicPt)]
- **<big>OrderedMap_ReverseIter</big>** : returns a channel that yields key-value pairs in reverse order.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_ReverseIter)]
[[play](https://go.dev/play/p/8Q0ssg6hZzO)]
- **<big>OrderedMap_SortByKey</big>** : sorts the map by key given less function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_SortByKey)]
[[play](https://go.dev/play/p/N7hjD_alZPq)]
- **<big>OrderedMap_MarshalJSON</big>** : implements the json.Marshaler interface.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_MarshalJSON)]
[[play](https://go.dev/play/p/C-wAwydIAC7)]
- **<big>OrderedMap_UnmarshalJSON</big>** : implements the json.Unmarshaler interface.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OrderedMap_UnmarshalJSON)]
[[play](https://go.dev/play/p/t_pkwerIRVx)]
- **<big>NewConcurrentMap</big>** : creates a ConcurrentMap with specific shard count.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewConcurrentMap)]
[[play](https://go.dev/play/p/3PenTPETJT0)]
@@ -936,6 +1014,13 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ConcurrentMap_Range</big>** : calls iterator sequentially for each key and value present in each of the shards in the map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ConcurrentMap_Range)]
[[play](https://go.dev/play/p/iqcy7P8P0Pr)]
- **<big>SortByKey</big>** : sorts the map by its keys and returns a new map with sorted keys.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#SortByKey)]
[[play](https://go.dev/play/p/PVdmBSnm6P_W)]
- **<big>GetOrDefault</big>** : returns the value of the given key or a default value if the key is not present.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#GetOrDefault)]
[[play](https://go.dev/play/p/99QjSYSBdiM)]
<h3 id="mathutil"> 13. Mathutil package implements some functions for math calculation. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1166,20 +1251,36 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>UUIdV4</big>** : generate a random UUID of version 4 according to RFC 4122.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#UUIdV4)]
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
- **<big>RandUniqueIntSlice</big>** : generate a slice of random int of length n that do not repeat.
- **<big>RandUniqueIntSlice</big>** : generate a slice of random int that do not repeat.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandUniqueIntSlice)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandSymbolChar</big>** : Generate a random symbol char of specified length.
- **<big>RandSymbolChar</big>** : generate a random symbol char of specified length.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandSymbolChar)]
[[play](https://go.dev/play/p/Im6ZJxAykOm)]
- **<big>RandFloat</big>** : Generate a random float64 number between [min, max) with specific precision.
- **<big>RandFloat</big>** : generate a random float64 number between [min, max) with specific precision.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloat)]
[[play](https://go.dev/play/p/zbD_tuobJtr)]
- **<big>RandFloats</big>** : Generate a slice of random float64 numbers of length n that do not repeat.
- **<big>RandFloats</big>** : generate a slice of random float64 numbers that do not repeat.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloats)]
[[play](https://go.dev/play/p/I3yndUQ-rhh)]
- **<big>RandStringSlice</big>** : generate a slice of random string of length strLen based on charset.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandStringSlice)]
[[play](https://go.dev/play/p/2_-PiDv3tGn)]
- **<big>RandBool</big>** : generate a random boolean value (true or false).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandBool)]
[[play](https://go.dev/play/p/to6BLc26wBv)]
- **<big>RandBoolSlice</big>** : generate a random boolean slice of specified length.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandBoolSlice)]
[[play](https://go.dev/play/p/o-VSjPjnILI)]
- **<big>RandIntSlice</big>** : generate a slice of random int. Number range in [min, max)
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandIntSlice)]
[[play](https://go.dev/play/p/GATTQ5xTEG8)]
- **<big>RandFromGivenSlice</big>** : generate a random element from given slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFromGivenSlice)]
[[play](https://go.dev/play/p/UrkWueF6yYo)]
- **<big>RandSliceFromGivenSlice</big>** : generate a random slice of length num from given slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandSliceFromGivenSlice)]
[[play](https://go.dev/play/p/68UikN9d6VT)]
<h3 id="retry"> 17. Retry package is for executing a function repeatedly until it was successful or canceled by the context. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1209,11 +1310,13 @@ import "github.com/duke-git/lancet/v2/retry"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#BackoffStrategy)]
- **<big>RetryWithCustomBackoff</big>** : set abitary custom backoff strategy.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithCustomBackoff)]
[[play](https://go.dev/play/p/jIm_o2vb5Y4)]
- **<big>RetryWithLinearBackoff</big>** : set linear strategy backoff.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithLinearBackoff)]
[[play](https://go.dev/play/p/PDet2ZQZwcB)]
- **<big>RetryWithExponentialWithJitterBackoff</big>** : set exponential strategy backoff.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
[[play](https://go.dev/play/p/xp1avQmn16X)]
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1320,6 +1423,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ForEachWithBreak</big>** : iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ForEachWithBreak)]
[[play](https://go.dev/play/p/qScs39f3D9W)]
- **<big>ForEachConcurrent</big>** : applies the iteratee function to each item in the slice concurrently.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ForEachConcurrent)]
[[play](https://go.dev/play/p/kT4XW7DKVoV)]
- **<big>GroupBy</big>** : iterate over elements of the slice, each element will be group by criteria, returns two slices.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#GroupBy)]
[[play](https://go.dev/play/p/QVkPxzPR0iA)]
@@ -1347,6 +1453,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Map</big>** : creates an slice of values by running each element of slice thru iteratee function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Map)]
[[play](https://go.dev/play/p/biaTefqPquw)]
- **<big>MapConcurrent</big>** : applies the iteratee function to each item in the slice by concrrent.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#MapConcurrent)]
[[play](https://go.dev/play/p/H1ehfPkPen0)]
- **<big>Merge</big>** : merge all given slices into one slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Merge)]
[[play](https://go.dev/play/p/lbjFp784r9N)]
@@ -1362,6 +1471,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ReduceRight</big>** : ReduceRight is like ReduceBy, but it iterates over elements of slice from right to left.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ReduceRight)]
[[play](https://go.dev/play/p/qT9dZC03A1K)]
- **<big>ReduceConcurrent</big>** : reduces the slice to a single value by applying the reducer function to each item in the slice concurrently.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#ReduceConcurrent)]
[[play](https://go.dev/play/p/Tjwe6OtaG07)]
- **<big>Replace</big>** : returns a copy of the slice with the first n non-overlapping instances of old replaced by new.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Replace)]
[[play](https://go.dev/play/p/P5mZp7IhOFo)]
@@ -1413,11 +1525,18 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Unique</big>** : remove duplicate elements in slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Unique)]
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
- **<big>UniqueBy</big>** : call iteratee func with every item of slice, then remove duplicated.
- **<big>UniqueBy</big>** : remove duplicate elements from the input slice based on the values returned by the iteratee function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueBy)]
[[play](https://go.dev/play/p/UR323iZLDpv)]
[[play](https://go.dev/play/p/GY7JE4yikrl)]
- **<big>UniqueByComparator</big>** : remove duplicate elements from the input slice using the provided comparator function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByComparator)]
[[play](https://go.dev/play/p/rwSacr-ZHsR)]
- **<big>UniqueByField</big>** : remove duplicate elements in struct slice by struct field.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByField)]
[[play](https://go.dev/play/p/6cifcZSPIGu)]
- **<big>UniqueByConcurrent</big>** : remove duplicate elements from the slice by concurrent.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByConcurrent)]
[[play](https://go.dev/play/p/wXZ7LcYRMGL)]
- **<big>Union</big>** : creates a slice of unique elements, in order, from all given slices.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Union)]
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
@@ -1453,6 +1572,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>LeftPadding</big>** : adds padding to the left begin of a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#LeftPadding)]
[[play](https://go.dev/play/p/jlQVoelLl2k)]
- **<big>Frequency</big>** : counts the frequency of each element in the slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Frequency)]
[[play](https://go.dev/play/p/CW3UVNdUZOq)]
<h3 id="stream"> 19. Stream package implements a sequence of elements supporting sequential and operations. this package is an experiment to explore if stream in go can work as the way java does. its function is very limited. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1487,6 +1609,9 @@ import "github.com/duke-git/lancet/v2/stream"
- **<big>Filter</big>** : returns a stream consisting of the elements of this stream that match the given predicate.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Filter)]
[[play](https://go.dev/play/p/MFlSANo-buc)]
- **<big>FilterConcurrent</big>** : Applies the provided filter function `predicate` to each element of the input slice concurrently.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FilterConcurrent)]
[[play](https://go.dev/play/p/t_pkwerIRVx)]
- **<big>Map</big>** : returns a stream consisting of the elements of this stream that apply the given function to elements of stream.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Map)]
[[play](https://go.dev/play/p/OtNQUImdYko)]
@@ -1704,6 +1829,24 @@ import "github.com/duke-git/lancet/v2/strutil"
- **<big>HammingDistance</big>** : calculates the Hamming distance between two strings.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#HammingDistance)]
[[play](https://go.dev/play/p/glNdQEA9HUi)]
- **<big>Concat</big>** : concatenates strings.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Concat)]
[[play](https://go.dev/play/p/gD52SZHr4Kp)]
- **<big>Ellipsis</big>** : truncates a string to a specified length and appends an ellipsis.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Ellipsis)]
[[play](https://go.dev/play/p/i1vbdQiQVRR)]
- **<big>Shuffle</big>** : shuffle the order of characters of given string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Shuffle)]
[[play](https://go.dev/play/p/iStFwBwyGY7)]
- **<big>Rotate</big>** : rotates the string by the specified number of characters.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#Rotate)]
[[play](https://go.dev/play/p/Kf03iOeT5bd)]
- **<big>TemplateReplace</big>** : replaces the placeholders in the template string with the corresponding values in the data map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#TemplateReplace)]
[[play](https://go.dev/play/p/cXSuFvyZqv9)]
- **<big>RegexMatchAllGroups</big>** : matches all subgroups in a string using a regular expression and returns the result.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#RegexMatchAllGroups)]
[[play](https://go.dev/play/p/JZiu0RXpgN-)]
<h3 id="system"> 22. System package contain some functions about os, runtime, shell command. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1741,6 +1884,19 @@ import "github.com/duke-git/lancet/v2/system"
- **<big>GetOsBits</big>** : return current os bits (32 or 64).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetOsBits)]
[[play](https://go.dev/play/p/ml-_XH3gJbW)]
- **<big>StartProcess</big>** : start a new process with the specified name and arguments.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#StartProcess)]
[[play](https://go.dev/play/p/5GVol6ryS_X)]
- **<big>StopProcess</big>** : stop a process by pid.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#StopProcess)]
[[play](https://go.dev/play/p/jJZhRYGGcmD)]
- **<big>KillProcess</big>** : kill a new process with the specified name and arguments.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#KillProcess)]
[[play](https://go.dev/play/p/XKmvV-ExBWa)]
- **<big>GetProcessInfo</big>** : retrieves detailed process information by pid.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/system.md#GetProcessInfo)]
[[play](https://go.dev/play/p/NQDVywEYYx7)]
<h3 id="tuple"> 23. Tuple package implements tuple data type and some operations on it. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -2044,11 +2200,15 @@ import "github.com/duke-git/lancet/v2/xerror"
## How to Contribute
#### [Contributing Guide](./CONTRIBUTING.md)
#### [Contribution Guide](./CONTRIBUTION.md)
## Contributors
Thank you to all the people who contributed to lancet!
<a href="https://github.com/duke-git/lancet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
</a>
</a>
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=duke-git/lancet&type=Date)](https://star-history.com/#duke-git/lancet&Date)

View File

@@ -4,12 +4,13 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.3.2-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.3-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)
[![codecov](https://codecov.io/gh/duke-git/lancet/branch/main/graph/badge.svg?token=FC48T1F078)](https://codecov.io/gh/duke-git/lancet)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/duke-git/lancet/blob/main/LICENSE)
[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20Lancet%20Guru-006BFF)](https://gurubase.io/g/lancet)
</div>
@@ -23,7 +24,7 @@
## 特性
- 👏 全面、高效、可复用。
- 💪 600+常用 go 工具函数,支持 string、slice、datetime、net、crypt...
- 💪 700+常用 go 工具函数,支持 string、slice、datetime、net、crypt...
- 💅 只依赖 go 标准库和 golang.org/x。
- 🌍 所有导出函数单元测试覆盖率 100%。
@@ -37,7 +38,7 @@
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
```
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.3。</b>
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.4。</b>
```go
go get github.com/duke-git/lancet// 使用go1.18以下版本, 必须安装v1.x.x版本
@@ -363,6 +364,12 @@ import "github.com/duke-git/lancet/v2/cryptor"
- **<big>AesOfbDecrypt</big>** : 使用 AES OFB 算法模式解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesOfbDecrypt)]
[[play](https://go.dev/play/p/VtHxtkUj-3F)]
- **<big>AesGcmEncrypt</big>** : 使用 AES GCM 算法模式加密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesGcmEncrypt)]
[[play](https://go.dev/play/p/rUt0-DmsPCs)]
- **<big>AesGcmDecrypt</big>** : 使用 AES GCM 算法模式解密数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#AesGcmDecrypt)]
[[play](https://go.dev/play/p/rUt0-DmsPCs)]
- **<big>Base64StdEncode</big>** : 将字符串 base64 编码。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#Base64StdEncode)]
[[play](https://go.dev/play/p/VOaUyQUreoK)]
@@ -472,7 +479,7 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/sSVmkfENKMz)]
<h3 id="datetime"> 7. datetime 日期时间处理包,格式化日期,比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
<h3 id="datetime"> 7. datetime日期时间处理包格式化日期比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/datetime"
@@ -606,6 +613,16 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>TimestampNano</big>** : 返回当前纳秒级时间戳。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#TimestampNano)]
[[play](https://go.dev/play/p/A9Oq_COrcCF)]
- **<big>TrackFuncTime</big>** : 测试函数执行时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#TrackFuncTime)]
[[play](https://go.dev/play/p/QBSEdfXHPTp)]
- **<big>DaysBetween</big>** : 返回两个日期之间的天数差。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#DaysBetween)]
[[play](https://go.dev/play/p/qD6qGb3TbOy)]
- **<big>GenerateDatetimesBetween</big>** : 生成从start到end的所有日期时间的字符串列表。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#GenerateDatetimesBetween)]
[[play](https://go.dev/play/p/6kHBpAxD9ZC)]
<h3 id="datastructure"> 8. datastructure 包含一些普通的数据结构实现。例如list, linklist, stack, queue, set, tree, graph。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -748,7 +765,7 @@ import "github.com/duke-git/lancet/v2/formatter"
#### 函数列表:
- **<big>Comma</big>** : 用逗号每隔 3 位分割数字/字符串,支持前缀添加符号。
- **<big>Comma</big>** : 用逗号每隔 3 位分割数字/字符串,支持添加前缀符号。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/formatter.md#Comma)]
[[play](https://go.dev/play/p/eRD5k2vzUVX)]
- **<big>Pretty</big>** : 返回 pretty JSON 字符串。
@@ -793,9 +810,15 @@ import "github.com/duke-git/lancet/v2/function"
- **<big>Delay</big>** : 延迟 delay 时间后调用函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Delay)]
[[play](https://go.dev/play/p/Ivtc2ZE-Tye)]
- **<big>Debounced</big>** : 创建一个 debounced 函数,该函数延迟调用 fn 直到自上次调用 debounced 函数后等待持续时间过去。
- **<big>Debounced<sup>deprecated</sup></big>** : 创建一个 debounced 函数,该函数延迟调用 fn 直到自上次调用 debounced 函数后等待持续时间过去。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Debounced)]
[[play](https://go.dev/play/p/absuEGB_GN7)]
- **<big>Debounce</big>** : 创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Debounce)]
[[play](https://go.dev/play/p/-dGFrYn_1Zi)]
- **<big>Throttle</big>** : 创建一个函数的节流版本。返回的函数保证在每个时间间隔内最多只会被调用一次。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Throttle)]
[[play](https://go.dev/play/p/HpoMov-tJSN)]
- **<big>Schedule</big>** : 每次持续时间调用函数,直到关闭返回的 channel。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Schedule)]
[[play](https://go.dev/play/p/hbON-Xeyn5N)]
@@ -902,9 +925,10 @@ import "github.com/duke-git/lancet/v2/maputil"
[[play](https://go.dev/play/p/N9qgYg_Ho6f)]
- **<big>HasKey</big>** : 检查 map 是否包含某个 key。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#HasKey)]
[[play](https://go.dev/play/p/isZZHOsDhFc)]
- **<big>GetOrSet</big>** : 返回给定键的值,如果不存在则设置该值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrSet)]
[[play](https://go.dev/play/p/isZZHOsDhFc)]
[[play](https://go.dev/play/p/IVQwO1OkEJC)]
- **<big>MapToStruct</big>** : 将map转成struct。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#MapToStruct)]
[[play](https://go.dev/play/p/7wYyVfX38Dp)]
@@ -914,6 +938,60 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ToSortedSlicesWithComparator</big>** : 将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片value切片中元素的位置与key对应。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesWithComparator)]
[[play](https://go.dev/play/p/0nlPo6YLdt3)]
- **<big>NewOrderedMap</big>** : 创建有序映射。有序映射是键值对的集合,其中键是唯一的,并且保留键插入的顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewOrderedMap)]
[[play](https://go.dev/play/p/Y4ZJ_oOc1FU)]
- **<big>OrderedMap_Set</big>** : 设置给定的键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Set)]
[[play](https://go.dev/play/p/Y4ZJ_oOc1FU)]
- **<big>OrderedMap_Get</big>** : 返回给定键的值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Get)]
[[play](https://go.dev/play/p/Y4ZJ_oOc1FU)]
- **<big>OrderedMap_Delete</big>** : 删除给定键的键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Delete)]
[[play](ttps://go.dev/play/p/5bIi4yaZ3K-)]
- **<big>OrderedMap_Clear</big>** : 清空map数据。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Clear)]
[[play](https://go.dev/play/p/8LwoJyEfuFr)]
- **<big>OrderedMap_Front</big>** : 返回第一个键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Front)]
[[play](https://go.dev/play/p/ty57XSimpoe)]
- **<big>OrderedMap_Back</big>** : 返回最后一个键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Back)]
[[play](https://go.dev/play/p/rQMjp1yQmpa)]
- **<big>OrderedMap_Range</big>** : 为每个键值对调用给定的函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Range)]
[[play](https://go.dev/play/p/U-KpORhc7LZ)]
- **<big>OrderedMap_Keys</big>** : 按顺序返回键的切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Keys)]
[[play](https://go.dev/play/p/Vv_y9ExKclA)]
- **<big>OrderedMap_Values</big>** : 按顺序返回值的切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Values)]
[[play](https://go.dev/play/p/TWj5n1-PUfx)]
- **<big>OrderedMap_Elements</big>** : 按顺序返回键值对。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Elements)]
[[play](https://go.dev/play/p/4BHG4kKz6bB)]
- **<big>OrderedMap_Len</big>** : 返回键值对的数量。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Len)]
[[play](https://go.dev/play/p/cLe6z2VX5N-)]
- **<big>OrderedMap_Contains</big>** : 如果给定的键存在则返回true。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Contains)]
[[play](https://go.dev/play/p/QuwqqnzwDNX)]
- **<big>OrderedMap_Iter</big>** : 返回按顺序产生键值对的通道。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_Iter)]
[[play](https://go.dev/play/p/tlq2tdvicPt)]
- **<big>OrderedMap_ReverseIter</big>** : 返回以相反顺序产生键值对的通道。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_ReverseIter)]
[[play](https://go.dev/play/p/8Q0ssg6hZzO)]
- **<big>OrderedMap_SortByKey</big>** : 使用传入的比较函数排序map key。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_SortByKey)]
[[play](https://go.dev/play/p/N7hjD_alZPq)]
- **<big>OrderedMap_MarshalJSON</big>** : 实现json.Marshaler接口。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_MarshalJSON)]
[[play](https://go.dev/play/p/C-wAwydIAC7)]
- **<big>OrderedMap_UnmarshalJSON</big>** : 实现json.Unmarshaler接口。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#OrderedMap_UnmarshalJSON)]
[[play](https://go.dev/play/p/8C3MvJ3-mut)]
- **<big>NewConcurrentMap</big>** : ConcurrentMap 协程安全的 map 结构。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewConcurrentMap)]
[[play](https://go.dev/play/p/3PenTPETJT0)]
@@ -938,6 +1016,12 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>ConcurrentMap_Range</big>** : 为 map 中每个键和值顺序调用迭代器。 如果 iterator 返回 false则停止迭代。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ConcurrentMap_Range)]
[[play](https://go.dev/play/p/iqcy7P8P0Pr)]
- **<big>SortByKey</big>** : 对传入的map根据key进行排序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#SortByKey)]
[[play](https://go.dev/play/p/PVdmBSnm6P_W)]
- **<big>GetOrDefault</big>** : 返回给定键的值,如果键不存在,则返回默认值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrDefault)]
[[play](https://go.dev/play/p/99QjSYSBdiM)]
<h3 id="mathutil"> 13. mathutil 包实现了一些数学计算的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1168,7 +1252,7 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>UUIdV4</big>** : 生成 UUID v4 字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#UUIdV4)]
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的长度为 n 的随机 int 切片。
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的随机int切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandUniqueIntSlice)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandSymbolChar</big>** : 生成给定长度的随机符号字符串。
@@ -1180,6 +1264,24 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>RandFloats</big>** : 生成随机float64数字切片可以指定长度范围和精度.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloats)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandStringSlice</big>** : 生成随机字符串slice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandStringSlice)]
[[play](https://go.dev/play/p/2_-PiDv3tGn)]
- **<big>RandBool</big>** : 生成随机bool值(true or false)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBool)]
[[play](https://go.dev/play/p/to6BLc26wBv)]
- **<big>RandBoolSlice</big>** : 生成特定长度的随机bool slice。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandBoolSlice)]
[[play](https://go.dev/play/p/o-VSjPjnILI)]
- **<big>RandIntSlice</big>** : 生成一个特定长度的随机int切片数值范围[min, max)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandIntSlice)]
[[play](https://go.dev/play/p/GATTQ5xTEG8)]
- **<big>RandFromGivenSlice</big>** : 从给定切片中随机生成元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFromGivenSlice)]
[[play](https://go.dev/play/p/UrkWueF6yYo)]
- **<big>RandSliceFromGivenSlice</big>** : 从给定切片中生成长度为 num 的随机切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandSliceFromGivenSlice)]
[[play](https://go.dev/play/p/68UikN9d6VT)]
<h3 id="retry"> 17. retry 重试执行函数直到函数运行成功或被 context cancel。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1199,9 +1301,6 @@ import "github.com/duke-git/lancet/v2/retry"
- **<big>RetryFunc</big>** : 重试执行的函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryFunc)]
[[play](https://go.dev/play/p/nk2XRmagfVF)]
- **<big>RetryDuration</big>** : 设置重试间隔时间,默认 3 秒。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryDuration)]
[[play](https://go.dev/play/p/nk2XRmagfVF)]
- **<big>RetryTimes</big>** : 设置重试次数,默认 5。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryTimes)]
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
@@ -1209,10 +1308,13 @@ import "github.com/duke-git/lancet/v2/retry"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#BackoffStrategy)]
- **<big>RetryWithCustomBackoff</big>** : 设置自定义退避策略。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithCustomBackoff)]
[[play](https://go.dev/play/p/jIm_o2vb5Y4)]
- **<big>RetryWithLinearBackoff</big>** : 设置线性策略退避。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithLinearBackoff)]
[[play](https://go.dev/play/p/PDet2ZQZwcB)]
- **<big>RetryWithExponentialWithJitterBackoff</big>** : 设置指数策略退避。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
[[play](https://go.dev/play/p/xp1avQmn16X)]
@@ -1317,6 +1419,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ForEach</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEach)]
[[play](https://go.dev/play/p/DrPaa4YsHRF)]
- **<big>ForEachConcurrent</big>** : 对slice并发执行foreach操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEachConcurrent)]
[[play](https://go.dev/play/p/kT4XW7DKVoV)]
- **<big>ForEachWithBreak</big>** : 遍历切片的元素并为每个元素调用 iteratee 函数,当 iteratee 函数返回 false 时,终止遍历。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ForEachWithBreak)]
[[play](https://go.dev/play/p/qScs39f3D9W)]
@@ -1347,6 +1452,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Map</big>** : 对 slice 中的每个元素执行 map 函数以创建一个新切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Map)]
[[play](https://go.dev/play/p/biaTefqPquw)]
- **<big>MapConcurrent</big>** : 对slice并发执行map操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#MapConcurrent)]
[[play](https://go.dev/play/p/H1ehfPkPen0)]
- **<big>Merge</big>** : 合并多个切片(不会消除重复元素)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Merge)]
[[play](https://go.dev/play/p/lbjFp784r9N)]
@@ -1362,6 +1470,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>ReduceRight</big>** : 类似 ReduceBy 操作,迭代切片元素顺序从右至左。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceRight)]
[[play](https://go.dev/play/p/qT9dZC03A1K)]
- **<big>ReduceConcurrent</big>** : 对切片元素执行并发reduce操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#ReduceConcurrent)]
[[play](https://go.dev/play/p/Tjwe6OtaG07)]
- **<big>Replace</big>** : 返回切片的副本,其中前 n 个不重叠的 old 替换为 new。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Replace)]
[[play](https://go.dev/play/p/P5mZp7IhOFo)]
@@ -1413,11 +1524,18 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Unique</big>** : 删除切片中的重复元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Unique)]
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
- **<big>UniqueBy</big>** : 对切片的每个元素调用 iteratee 函数,然后删除重复元素
- **<big>UniqueBy</big>** : 根据迭代函数返回的值,从输入切片中移除重复元素。此函数保持元素的顺序
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueBy)]
[[play](https://go.dev/play/p/UR323iZLDpv)]
- **<big>UniqueByField</big>** : 根据struct字段对struct切片去重复
[[play](https://go.dev/play/p/GY7JE4yikrl)]
- **<big>UniqueByComparator</big>** : 使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByComparator)]
[[play](https://go.dev/play/p/rwSacr-ZHsR)]
- **<big>UniqueByField</big>** : 根据struct字段对struct切片去重复。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByField)]
[[play](https://go.dev/play/p/6cifcZSPIGu)]
- **<big>UniqueByConcurrent</big>** : 并发的从输入切片中移除重复元素,结果保持元素的顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByConcurrent)]
[[play](https://go.dev/play/p/wXZ7LcYRMGL)]
- **<big>Union</big>** : 合并多个切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Union)]
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
@@ -1452,7 +1570,9 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>LeftPadding</big>** : 在切片的左部添加元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#LeftPadding)]
[[play](https://go.dev/play/p/jlQVoelLl2k)]
- **<big>Frequency</big>** : 计算切片中每个元素出现的频率。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Frequency)]
[[play](https://go.dev/play/p/CW3UVNdUZOq)]
<h3 id="stream"> 19. stream 流,该包仅验证简单的 stream 实现,功能有限。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1481,12 +1601,15 @@ import "github.com/duke-git/lancet/v2/stream"
- **<big>Concat</big>** : 创建一个延迟连接 stream其元素是第一个 stream 的所有元素,后跟第二个 stream 的全部元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Concat)]
[[play](https://go.dev/play/p/HM4OlYk_OUC)]
- **<big>Distinct</big>** : 创建并返回一个 stream用于删除重复的项。
- **<big>Distinct</big>** : 创建并返回一个stream用于删除重复的项。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Distinct)]
[[play](https://go.dev/play/p/eGkOSrm64cB)]
- **<big>Filter</big>** : 返回一个通过判定函数的 stream。
- **<big>Filter</big>** : 返回一个通过判定函数的stream。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Filter)]
[[play](https://go.dev/play/p/MFlSANo-buc)]
- **<big>FilterConcurrent</big>** : 对slice并发执行filter操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#FilterConcurrent)]
[[play](https://go.dev/play/p/t_pkwerIRVx)]
- **<big>Map</big>** : 返回一个 stream该 stream 由将给定函数应用于源 stream 元素的元素组成。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#Map)]
[[play](https://go.dev/play/p/OtNQUImdYko)]
@@ -1708,7 +1831,24 @@ import "github.com/duke-git/lancet/v2/strutil"
- **<big>HammingDistance</big>** : 计算两个字符串之间的汉明距离。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HammingDistance)]
[[play](https://go.dev/play/p/glNdQEA9HUi)]
- **<big>Concat</big>** : 拼接字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Concat)]
[[play](https://go.dev/play/p/gD52SZHr4Kp)]
- **<big>Ellipsis</big>** : 将字符串截断到指定长度,并在末尾添加省略号。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Ellipsis)]
[[play](https://go.dev/play/p/i1vbdQiQVRR)]
- **<big>Shuffle</big>** : 打乱给定字符串中的字符顺序。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Shuffle)]
[[play](https://go.dev/play/p/iStFwBwyGY7)]
- **<big>Rotate</big>** : 按指定的字符数旋转字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#Rotate)]
[[play](https://go.dev/play/p/Kf03iOeT5bd)]
- **<big>TemplateReplace</big>** : 将模板字符串中的占位符替换为map中的相应值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#TemplateReplace)]
[[play](https://go.dev/play/p/cXSuFvyZqv9)]
- **<big>RegexMatchAllGroups</big>** : 使用正则表达式匹配字符串中的所有子组并返回结果。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RegexMatchAllGroups)]
[[play](https://go.dev/play/p/JZiu0RXpgN-)]
<h3 id="system"> 22. system 包含 os, runtime, shell command 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1745,6 +1885,20 @@ import "github.com/duke-git/lancet/v2/system"
- **<big>GetOsBits</big>** : 获取当前操作系统位数(32/64)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system#GetOsBits)]
[[play](https://go.dev/play/p/ml-_XH3gJbW)]
- **<big>StartProcess</big>** :创建进程。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#StartProcess)]
[[play](https://go.dev/play/p/5GVol6ryS_X)]
- **<big>StopProcess</big>** : 停止进程。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#StopProcess)]
[[play](https://go.dev/play/p/jJZhRYGGcmD)]
- **<big>KillProcess</big>** : 杀掉进程。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#KillProcess)]
[[play](https://go.dev/play/p/XKmvV-ExBWa)]
- **<big>GetProcessInfo</big>** : 根据进程id获取进程信息。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/system.md#GetProcessInfo)]
[[play](https://go.dev/play/p/NQDVywEYYx7)]
<h3 id="tuple"> 23. Tuple 包实现一个元组数据类型。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1980,7 +2134,7 @@ import "github.com/duke-git/lancet/v2/validator"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsBase64URL)]
[[play](https://go.dev/play/p/vhl4mr8GZ6S)]
- **<big>IsJWT</big>** : 检查字符串是否是有效的 JSON Web Token (JWT)。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsJWT)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsJWT)]
[[play](https://go.dev/play/p/R6Op7heJbKI)]
- **<big>IsVisa</big>** : 检查字符串是否是有效的 visa 卡号。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/validator.md#IsVisa)]
@@ -2048,7 +2202,7 @@ import "github.com/duke-git/lancet/v2/xerror"
## 如何贡献代码
#### [贡献代码指南](./CONTRIBUTING.zh-CN.md)
#### [代码贡献指南](./CONTRIBUTION.zh-CN.md)
## 贡献者
@@ -2057,3 +2211,7 @@ import "github.com/duke-git/lancet/v2/xerror"
<a href="https://github.com/duke-git/lancet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
</a>
## GitHub Stars
[![Star History Chart](https://api.star-history.com/svg?repos=duke-git/lancet&type=Date)](https://star-history.com/#duke-git/lancet&Date)

View File

@@ -44,7 +44,7 @@ func (l *LRUCache[K, V]) Get(key K) (V, bool) {
node, ok := l.cache[key]
if ok {
l.moveToHead(node)
l.moveToTail(node)
return node.value, true
}
@@ -66,7 +66,7 @@ func (l *LRUCache[K, V]) Put(key K, value V) {
}
} else {
node.value = value
l.moveToHead(node)
l.moveToTail(node)
}
l.length = len(l.cache)
}
@@ -79,7 +79,7 @@ func (l *LRUCache[K, V]) Delete(key K) bool {
delete(l.cache, key)
return true
}
l.length = len(l.cache)
return false
}
@@ -112,7 +112,7 @@ func (l *LRUCache[K, V]) deleteNode(node *lruNode[K, V]) K {
return node.key
}
func (l *LRUCache[K, V]) moveToHead(node *lruNode[K, V]) {
func (l *LRUCache[K, V]) moveToTail(node *lruNode[K, V]) {
if l.tail == node {
return
}

View File

@@ -8,7 +8,7 @@ import (
func TestLRUCache(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestLRUCache")
assert := internal.NewAssert(t, "TestLRUCache")
cache := NewLRUCache[int, int](3)
@@ -16,19 +16,19 @@ func TestLRUCache(t *testing.T) {
cache.Put(2, 2)
cache.Put(3, 3)
asssert.Equal(3, cache.Len())
assert.Equal(3, cache.Len())
v, ok := cache.Get(1)
asssert.Equal(true, ok)
asssert.Equal(1, v)
assert.Equal(true, ok)
assert.Equal(1, v)
v, ok = cache.Get(2)
asssert.Equal(true, ok)
asssert.Equal(2, v)
assert.Equal(true, ok)
assert.Equal(2, v)
ok = cache.Delete(2)
asssert.Equal(true, ok)
assert.Equal(true, ok)
_, ok = cache.Get(2)
asssert.Equal(false, ok)
assert.Equal(false, ok)
}

View File

@@ -8,34 +8,34 @@ import (
func TestLinearSearch(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestLinearSearch")
assert := internal.NewAssert(t, "TestLinearSearch")
numbers := []int{3, 4, 5, 3, 2, 1}
equalFunc := func(a, b int) bool {
return a == b
}
asssert.Equal(0, LinearSearch(numbers, 3, equalFunc))
asssert.Equal(-1, LinearSearch(numbers, 6, equalFunc))
assert.Equal(0, LinearSearch(numbers, 3, equalFunc))
assert.Equal(-1, LinearSearch(numbers, 6, equalFunc))
}
func TestBinarySearch(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestBinarySearch")
assert := internal.NewAssert(t, "TestBinarySearch")
sortedNumbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
comparator := &intComparator{}
asssert.Equal(4, BinarySearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator))
asssert.Equal(-1, BinarySearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator))
assert.Equal(4, BinarySearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator))
assert.Equal(-1, BinarySearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator))
}
func TestBinaryIterativeSearch(t *testing.T) {
asssert := internal.NewAssert(t, "TestBinaryIterativeSearch")
assert := internal.NewAssert(t, "TestBinaryIterativeSearch")
sortedNumbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
comparator := &intComparator{}
asssert.Equal(4, BinaryIterativeSearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator))
asssert.Equal(-1, BinaryIterativeSearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator))
assert.Equal(4, BinaryIterativeSearch(sortedNumbers, 5, 0, len(sortedNumbers)-1, comparator))
assert.Equal(-1, BinaryIterativeSearch(sortedNumbers, 9, 0, len(sortedNumbers)-1, comparator))
}

View File

@@ -47,7 +47,7 @@ func (c *intComparator) Compare(v1 any, v2 any) int {
func TestBubbleSortForStructSlice(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestBubbleSortForStructSlice")
assert := internal.NewAssert(t, "TestBubbleSortForStructSlice")
peoples := []people{
{Name: "a", Age: 20},
@@ -62,23 +62,23 @@ func TestBubbleSortForStructSlice(t *testing.T) {
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
assert.Equal(expected, actual)
}
func TestBubbleSortForIntSlice(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestBubbleSortForIntSlice")
assert := internal.NewAssert(t, "TestBubbleSortForIntSlice")
numbers := []int{2, 1, 5, 3, 6, 4}
comparator := &intComparator{}
BubbleSort(numbers, comparator)
asssert.Equal([]int{1, 2, 3, 4, 5, 6}, numbers)
assert.Equal([]int{1, 2, 3, 4, 5, 6}, numbers)
}
func TestInsertionSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestInsertionSort")
assert := internal.NewAssert(t, "TestInsertionSort")
peoples := []people{
{Name: "a", Age: 20},
@@ -93,12 +93,12 @@ func TestInsertionSort(t *testing.T) {
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
assert.Equal(expected, actual)
}
func TestSelectionSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestSelectionSort")
assert := internal.NewAssert(t, "TestSelectionSort")
peoples := []people{
{Name: "a", Age: 20},
@@ -113,12 +113,12 @@ func TestSelectionSort(t *testing.T) {
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
assert.Equal(expected, actual)
}
func TestShellSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestShellSort")
assert := internal.NewAssert(t, "TestShellSort")
peoples := []people{
{Name: "a", Age: 20},
@@ -133,12 +133,12 @@ func TestShellSort(t *testing.T) {
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
assert.Equal(expected, actual)
}
func TestQuickSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestQuickSort")
assert := internal.NewAssert(t, "TestQuickSort")
peoples := []people{
{Name: "a", Age: 20},
@@ -153,12 +153,12 @@ func TestQuickSort(t *testing.T) {
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
assert.Equal(expected, actual)
}
func TestHeapSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestHeapSort")
assert := internal.NewAssert(t, "TestHeapSort")
peoples := []people{
{Name: "a", Age: 20},
@@ -173,12 +173,12 @@ func TestHeapSort(t *testing.T) {
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
assert.Equal(expected, actual)
}
func TestMergeSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestMergeSort")
assert := internal.NewAssert(t, "TestMergeSort")
peoples := []people{
{Name: "a", Age: 20},
@@ -193,12 +193,12 @@ func TestMergeSort(t *testing.T) {
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", peoples)
asssert.Equal(expected, actual)
assert.Equal(expected, actual)
}
func TestCountSort(t *testing.T) {
t.Parallel()
asssert := internal.NewAssert(t, "TestCountSort")
assert := internal.NewAssert(t, "TestCountSort")
peoples := []people{
{Name: "a", Age: 20},
@@ -213,5 +213,5 @@ func TestCountSort(t *testing.T) {
expected := "[{d 8} {b 10} {c 17} {a 20} {e 28}]"
actual := fmt.Sprintf("%v", sortedPeopleByAge)
asssert.Equal(expected, actual)
assert.Equal(expected, actual)
}

View File

@@ -49,9 +49,7 @@ func Or[T, U any](a T, b U) bool {
// Xor returns true if a or b but not both is truthy.
// Play: https://go.dev/play/p/gObZrW7ZbG8
func Xor[T, U any](a T, b U) bool {
valA := Bool(a)
valB := Bool(b)
return (valA || valB) && valA != valB
return Bool(a) != Bool(b)
}
// Nor returns true if neither a nor b is truthy.
@@ -63,9 +61,7 @@ func Nor[T, U any](a T, b U) bool {
// Xnor returns true if both a and b or neither a nor b are truthy.
// Play: https://go.dev/play/p/OuDB9g51643
func Xnor[T, U any](a T, b U) bool {
valA := Bool(a)
valB := Bool(b)
return (valA && valB) || (!valA && !valB)
return Bool(a) == Bool(b)
}
// Nand returns false if both a and b are truthy.
@@ -76,10 +72,17 @@ func Nand[T, U any](a T, b U) bool {
// TernaryOperator checks the value of param `isTrue`, if true return ifValue else return elseValue.
// Play: https://go.dev/play/p/ElllPZY0guT
func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U {
func Ternary[T, U any](isTrue T, ifValue U, elseValue U) U {
if Bool(isTrue) {
return ifValue
} else {
return elseValue
}
}
// TernaryOperator checks the value of param `isTrue`, if true return ifValue else return elseValue.
// Play: https://go.dev/play/p/ElllPZY0guT
// Deprecated: Use Ternary instead.
func TernaryOperator[T, U any](isTrue T, ifValue U, elseValue U) U {
return Ternary(isTrue, ifValue, elseValue)
}

View File

@@ -148,13 +148,13 @@ func ExampleNand() {
// false
}
func ExampleTernaryOperator() {
func ExampleTernary() {
conditionTrue := 2 > 1
result1 := TernaryOperator(conditionTrue, 0, 1)
result1 := Ternary(conditionTrue, 0, 1)
fmt.Println(result1)
conditionFalse := 2 > 3
result2 := TernaryOperator(conditionFalse, 0, 1)
result2 := Ternary(conditionFalse, 0, 1)
fmt.Println(result2)
// Output:

View File

@@ -124,13 +124,13 @@ func TestNand(t *testing.T) {
assert.Equal(false, Nand(1, 1))
}
func TestTernaryOperator(t *testing.T) {
func TestTernary(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TernaryOperator")
assert := internal.NewAssert(t, "TestTernary")
trueValue := "1"
falseValue := "0"
assert.Equal(trueValue, TernaryOperator(true, trueValue, falseValue))
assert.Equal(trueValue, Ternary(true, trueValue, falseValue))
}

View File

@@ -8,16 +8,20 @@ package cryptor
import (
"bytes"
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/sha512"
"crypto/x509"
"encoding/pem"
"errors"
"io"
"os"
"strings"
)
// AesEcbEncrypt encrypt data with key use AES ECB algorithm
@@ -244,6 +248,56 @@ func AesOfbDecrypt(data, key []byte) []byte {
return decrypted
}
// AesGcmEncrypt encrypt data with key use AES GCM algorithm
// Play: https://go.dev/play/p/rUt0-DmsPCs
func AesGcmEncrypt(data, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err)
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
panic(err)
}
ciphertext := gcm.Seal(nonce, nonce, data, nil)
return ciphertext
}
// AesGcmDecrypt decrypt data with key use AES GCM algorithm
// Play: https://go.dev/play/p/rUt0-DmsPCs
func AesGcmDecrypt(data, key []byte) []byte {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
panic(err)
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
panic("ciphertext too short")
}
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
panic(err)
}
return plaintext
}
// DesEcbEncrypt encrypt data with key use DES ECB algorithm
// len(key) should be 8.
// Play: https://go.dev/play/p/8qivmPeZy4P
@@ -541,6 +595,7 @@ func RsaEncrypt(data []byte, pubKeyFileName string) []byte {
if err != nil {
panic(err)
}
return cipherText
}
@@ -574,6 +629,7 @@ func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
if err != nil {
panic(err)
}
return plainText
}
@@ -605,3 +661,150 @@ func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte
return decryptedBytes, nil
}
// RsaSign signs the data with RSA.
// Play: todo
func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error) {
privateKey, err := loadRasPrivateKey(privateKeyFileName)
if err != nil {
return nil, err
}
hashed, err := hashData(hash, data)
if err != nil {
return nil, err
}
return rsa.SignPKCS1v15(rand.Reader, privateKey, hash, hashed)
}
// RsaVerifySign verifies the signature of the data with RSA.
// Play: todo
func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error {
publicKey, err := loadRsaPublicKey(pubKeyFileName)
if err != nil {
return err
}
hashed, err := hashData(hash, data)
if err != nil {
return err
}
return rsa.VerifyPKCS1v15(publicKey, hash, hashed, signature)
}
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
func loadRsaPublicKey(filename string) (*rsa.PublicKey, error) {
pubKeyData, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(pubKeyData)
if block == nil {
return nil, errors.New("failed to decode PEM block containing the public key")
}
var pubKey *rsa.PublicKey
blockType := strings.ToUpper(block.Type)
if blockType == "RSA PUBLIC KEY" {
pubKey, err = x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
// todo: here should be a bug, should return nil, err
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
var ok bool
pubKey, ok = key.(*rsa.PublicKey)
if !ok {
return nil, errors.New("failed to parse RSA private key")
}
}
} else if blockType == "PUBLIC KEY" {
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
var ok bool
pubKey, ok = key.(*rsa.PublicKey)
if !ok {
return nil, errors.New("failed to parse RSA private key")
}
} else {
return nil, errors.New("unsupported key type")
}
return pubKey, nil
}
// loadRsaPrivateKey loads and parses a PEM encoded private key file.
func loadRasPrivateKey(filename string) (*rsa.PrivateKey, error) {
priKeyData, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(priKeyData)
if block == nil {
return nil, errors.New("failed to decode PEM block containing the private key")
}
var privateKey *rsa.PrivateKey
blockType := strings.ToUpper(block.Type)
// PKCS#1 format
if blockType == "RSA PRIVATE KEY" {
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
} else if blockType == "PRIVATE KEY" { // PKCS#8 format
priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
var ok bool
privateKey, ok = priKey.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("failed to parse RSA private key")
}
} else {
return nil, errors.New("unsupported key type")
}
return privateKey, nil
}
// hashData returns the hash value of the data, using the specified hash function
func hashData(hash crypto.Hash, data []byte) ([]byte, error) {
if !hash.Available() {
return nil, errors.New("unsupported hash algorithm")
}
var hashed []byte
switch hash {
case crypto.SHA224:
h := sha256.Sum224(data)
hashed = h[:]
case crypto.SHA256:
h := sha256.Sum256(data)
hashed = h[:]
case crypto.SHA384:
h := sha512.Sum384(data)
hashed = h[:]
case crypto.SHA512:
h := sha512.Sum512(data)
hashed = h[:]
default:
return nil, errors.New("unsupported hash algorithm")
}
return hashed, nil
}

View File

@@ -1,6 +1,7 @@
package cryptor
import (
"crypto"
"fmt"
)
@@ -129,6 +130,34 @@ func ExampleAesOfbDecrypt() {
// hello
}
func ExampleAesGcmEncrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesGcmEncrypt([]byte(data), []byte(key))
decrypted := AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleAesGcmDecrypt() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := AesGcmEncrypt([]byte(data), []byte(key))
decrypted := AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
func ExampleDesEcbEncrypt() {
data := "hello"
key := "abcdefgh"
@@ -256,7 +285,7 @@ func ExampleDesOfbDecrypt() {
func ExampleGenerateRsaKey() {
// Create ras private and public pem file
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
err := GenerateRsaKey(4096, "rsa_private_example.pem", "rsa_public_example.pem")
if err != nil {
return
}
@@ -269,14 +298,14 @@ func ExampleGenerateRsaKey() {
func ExampleRsaEncrypt() {
// Create ras private and public pem file
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
err := GenerateRsaKey(4096, "rsa_private_example.pem", "rsa_public_example.pem")
if err != nil {
return
}
data := []byte("hello")
encrypted := RsaEncrypt(data, "rsa_public.pem")
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
encrypted := RsaEncrypt(data, "rsa_public_example.pem")
decrypted := RsaDecrypt(encrypted, "rsa_private_example.pem")
fmt.Println(string(decrypted))
@@ -286,14 +315,14 @@ func ExampleRsaEncrypt() {
func ExampleRsaDecrypt() {
// Create ras private and public pem file
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
err := GenerateRsaKey(4096, "rsa_private_example.pem", "rsa_public_example.pem")
if err != nil {
return
}
data := []byte("hello")
encrypted := RsaEncrypt(data, "rsa_public.pem")
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
encrypted := RsaEncrypt(data, "rsa_public_example.pem")
decrypted := RsaDecrypt(encrypted, "rsa_private_example.pem")
fmt.Println(string(decrypted))
@@ -508,3 +537,49 @@ func ExampleRsaEncryptOAEP() {
// Output:
// hello world
}
func ExampleRsaSign() {
data := []byte("This is a test data for RSA signing")
hash := crypto.SHA256
privateKey := "./rsa_private_example.pem"
publicKey := "./rsa_public_example.pem"
signature, err := RsaSign(hash, data, privateKey)
if err != nil {
return
}
err = RsaVerifySign(hash, data, signature, publicKey)
if err != nil {
return
}
fmt.Println("ok")
// Output:
// ok
}
func ExampleRsaVerifySign() {
data := []byte("This is a test data for RSA signing")
hash := crypto.SHA256
privateKey := "./rsa_private_example.pem"
publicKey := "./rsa_public_example.pem"
signature, err := RsaSign(hash, data, privateKey)
if err != nil {
return
}
err = RsaVerifySign(hash, data, signature, publicKey)
if err != nil {
return
}
fmt.Println("ok")
// Output:
// ok
}

View File

@@ -1,6 +1,7 @@
package cryptor
import (
"crypto"
"testing"
"github.com/duke-git/lancet/v2/internal"
@@ -139,13 +140,13 @@ func TestDesOfbEncrypt(t *testing.T) {
func TestRsaEncrypt(t *testing.T) {
t.Parallel()
err := GenerateRsaKey(4096, "rsa_private.pem", "rsa_public.pem")
err := GenerateRsaKey(4096, "./rsa_private_example.pem", "./rsa_public_example.pem")
if err != nil {
t.FailNow()
}
data := []byte("hello world")
encrypted := RsaEncrypt(data, "rsa_public.pem")
decrypted := RsaDecrypt(encrypted, "rsa_private.pem")
encrypted := RsaEncrypt(data, "./rsa_public_example.pem")
decrypted := RsaDecrypt(encrypted, "./rsa_private_example.pem")
assert := internal.NewAssert(t, "TestRsaEncrypt")
assert.Equal(string(data), string(decrypted))
@@ -168,3 +169,66 @@ func TestRsaEncryptOAEP(t *testing.T) {
assert.IsNil(err)
assert.Equal("hello world", string(decrypted))
}
func TestAesGcmEncrypt(t *testing.T) {
t.Parallel()
data := "hello world"
key := "abcdefghijklmnop"
encrypted := AesGcmEncrypt([]byte(data), []byte(key))
decrypted := AesGcmDecrypt(encrypted, []byte(key))
assert := internal.NewAssert(t, "TestAesGcmEncrypt")
assert.Equal(data, string(decrypted))
}
func TestRsaSignAndVerify(t *testing.T) {
t.Parallel()
data := []byte("This is a test data for RSA signing")
hash := crypto.SHA256
t.Run("RSA Sign and Verify", func(t *testing.T) {
privateKey := "./rsa_private_example.pem"
publicKey := "./rsa_public_example.pem"
signature, err := RsaSign(hash, data, privateKey)
if err != nil {
t.Fatalf("RsaSign failed: %v", err)
}
err = RsaVerifySign(hash, data, signature, publicKey)
if err != nil {
t.Fatalf("RsaVerifySign failed: %v", err)
}
})
t.Run("RSA Sign and Verify Invalid Signature", func(t *testing.T) {
publicKey := "./rsa_public_example.pem"
invalidSig := []byte("InvalidSignature")
err := RsaVerifySign(hash, data, invalidSig, publicKey)
if err == nil {
t.Fatalf("RsaVerifySign failed: %v", err)
}
})
t.Run("RSA Sign and Verify With Different Hash", func(t *testing.T) {
publicKey := "./rsa_public_example.pem"
privateKey := "./rsa_private_example.pem"
hashSign := crypto.SHA256
hashVerify := crypto.SHA512
signature, err := RsaSign(hashSign, data, privateKey)
if err != nil {
t.Fatalf("RsaSign failed: %v", err)
}
err = RsaVerifySign(hashVerify, data, signature, publicKey)
if err == nil {
t.Fatalf("RsaVerifySign failed: %v", err)
}
})
}

View File

@@ -0,0 +1,51 @@
-----BEGIN rsa private key-----
MIIJKAIBAAKCAgEAymvyNgI0T1P/DnFA2FomMrx4SzBf9p4VJ70AyYmbdeGhtzh+
u6zNoDQ2UMmDzcCbtv9JW7PX80O8xy/O006geNPfW74T5ryH2GEj3Y+eOtIzYI6g
Qy74JXqiqEztE6BkvesYHC+Yo8IIrawBBYHgGvluAudTZPqnkcqnUd8ngfhahCQr
Mi+sKqiqOWC/D0FVSclfugWw5/EBxJJSBSPYumLJelbk3PqXtVm46mvGnlQlfHGp
pYQ5Nj6zKilCgoJoAHURp4dAaOwAhVOtc31M0WPre/9th4TodZu1GeqFt5HqmMeP
5+p2GDNDSxDNP3lgCgSqwRzy4pYaE5e70aznOs5iaclAAlrLN3aSK6lDT+BGQbQr
QgM47x3flMfszqNeuaBwdiNyzEodhT8eiYl8m08B1sVI811q7pG8WUVZjIYs2BzV
jK/VDnvbEyayX+RGCAXr5M/bfDM1YRfVS9TWxspQNB3pyxgLX//LJIATbutUVKcr
jxjoS7GdEocSruTOm/J+ZMTm0EqhsbRHVm+7GcsSLaiSACEq+ONQJsN1x1o2GGVE
i3pEZnDITSsokdcyrBj1A9a5Hx4tSiMbg88gS4MPOALzBTmEVneQC+U5HTG/erUp
Zk6/AhT9Flk3HQTNIdzz8a2Clfeh4GU1JoKB4u++XPt4M5h75OWnd5r9HYUCAwEA
AQKCAgEAkI/PBytD2HOQb+wJ93lKVmmrL2d44VO8oAinC0evMtzU9VjviXC72XHw
aHnCG6s2idZ/uXITA7SYmVhXSSmaCTCnD4oMBHkYOzwEtTLgOfnsn0S8x74/keSn
TbLCjYW67Ld9HIQRasIkGIQCpsA+IIWKP6CdOjyYd9JW9G1+dZ+8ZSq/frP1LPIP
v7KMQITUOEIMj3mJAdxo+s3Urb8QBOyQH0L3Z6m/ttBA84nM6z4FF37FPWPUyBy3
L6EP0sz3IXx6Az7gjQ4ewRklgpk1x0So5IFi46nTqkptZ/jJTnRzKnE7INGdTWMu
5+kz6+Tu5bi3ifr2q0Ovk9aFWBU3fM2UubQcWblnXdxulvHkrKiEKNhW4RAm4dOp
BunYvvIhnCMPj06fMcJEXDXjqdx5YaNHH/HuSKGUugGpeieG19xDvibBD9bTjfTC
RXmzieyRoVc2JoOoaqwfI/lgRiaxfVgDcAjhMo2OXYZfGlxjlahyyF4Fy3MUvBeW
yD3j/OTHEvaUY9fqMjVwc+p/5uFh7uR39mIVyhZSQJw5qjuGSTuPU7lNBh1uOVD/
MeJyh3hlPgzOpWleGoiXxliq1z57eZNT3hNUO1WqcUvTFUUq6gEHlgAoFB66KEGb
Xb4PJ2KGNjhOJtKbVPw13qR+2J6V9PPZ/vFRhdhBAhDht6tLjnECggEBAPghirHf
EJCg1Gg48FFfuOr/FqrCFC6j+lCR9KcvHSAXLUYITmFgbJR50R/17s8mmCOkq8mS
NMka3HNZyJ/CUvYDzvHsEqYKs5f3uTzb3Mrjp/hRyZKZ+h5o8oBPGqb2yC5KjfgR
yEd3XX5R+tFhC1oOOe5jNMf/exJf18yAOzwqVJ0hpu+ok7G8Lf5ggWgg05M0tVgs
vtyr+IQGOhWsCzi3cRu9HyDgunKeIQvk5Rz0mjIYWn9MwsIx9hnrcw/XpmAgG+HD
hwHODnXRfYCYEQfbyrIg4yrO7Uhu2NG301Hf8p/i8EjU7AeZyXQM3QvTMIPPRDmG
cK1w8Bz05uQGHUMCggEBANDXT838r/KigPcLp3bOLbdV6CExuzbh043+Qhmyryf0
qQKh0GKq/voQURDI/zFste9NqQqOoJAVMrRdYX5FB+HKgfHsQ1IfvhBHPHF6mkos
+zXtQc0XP4CKRV657/F4cYz8voTKJKVMKnlD80uZPUesGOxh0n1dkB9dnEf+RoqJ
gDVbi/391jU/Vw/0WHJX3yK3A48vkHH1LETNqco0kDXtGZOxY6wAJ03dfpK2/FPI
HBYBmiLJTRLJH3s71iZAOLabFsB970ZyyrfhbK1O8TASg0WTgTMhgqFZA3pX5hO6
4cDLb8mzjSXnFM6PMnu+fujo2Jd/YqSk5v190DNTiZcCggEAHh9USwudYzFjF9Px
uK86L60P/2LYOGFHvgg5/yHFE2Q85seTXFbsV4oCTTL57sPsrEcNY8cQCWntYUOB
C4P3tk34DX5vNSEPdF9qaWz3fNnuRkMHiXiP2Kk85z6zKZnD63q5iWf/PE3NV8xz
+n8hdalMdxgsDCuDsVNZS0Y16rPo2bqAHZAFfgouOzdT/mQdyz0W1sF32io2XTC0
VHUyV4xNeuSWptMhT1DLCjqbZcx7+6DhO5sB+bk++x6ONVokpH7BY8Ls3Nc7AiqQ
ZdAQITgZf05mxYehXq22PJ9oVAQv3CEcsnrGvJV600/MdecJeLbsvV8IxsVzINDK
RtxHKQKCAQAGfbrAR0tcukpR423YFn57RVNKvNX51bkSn8WEMPaawlMCfu8QMgps
0VcDs4ujCKL7Binr5xT8hXwm+QQPvauKDBZP460P/2aT8PLjABGNnqpMOcyiyEc0
Apg3YoYftkOpQy3UyMesz5o+XKtSPTgXYzT/G+dD+EWDhBBYeIHOyolOn0LRqTMg
QpC9MTYSj8KivJeCutK9iAZROSc+3rVgx7bUzV2wuex+0hSeEMv0+rJMyM32qNUZ
cWDmHq0AUVyx6E4ju4ZVZToBzyLmnB6JBPpJjlUktrTtuOuPwO2ozVU4/dnCpi8L
74vJA9Bo4jnlmV8qDk6NmYaIeIGhJsaPAoIBADVCmkCaMkySTHoC2icsinuQ2zQK
IkpuE7QxhD2r1WWSmsnKaP1WmYyfrcQefzBd4UN+0Amgq74qG9qfxXEzss1mAu+N
umO3suQ5BpFN9iVbmdAWC7SqAClPEZFHBMOd9B6nyBnv0Xp3Pclpk/ThPUTm2sxd
YvFEa8itw9NM0P2lXAYMM+ksFDThcf+sK+510+Du6aj/CZ5YhutRN0tbwNzN7ewT
QBGpCuEJ9XUjGxmE5YmKSDMPgBLOnjb4tywWwlYsndOnMpN6vpi+OZaZnZBqTiqF
9IZyUBoN+yHrFVF+O6ZZiSzSz8xSVhwoLTrBX3D2qrP02jg4sG1coiEX6iE=
-----END rsa private key-----

View File

@@ -0,0 +1,14 @@
-----BEGIN rsa public key-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAymvyNgI0T1P/DnFA2Fom
Mrx4SzBf9p4VJ70AyYmbdeGhtzh+u6zNoDQ2UMmDzcCbtv9JW7PX80O8xy/O006g
eNPfW74T5ryH2GEj3Y+eOtIzYI6gQy74JXqiqEztE6BkvesYHC+Yo8IIrawBBYHg
GvluAudTZPqnkcqnUd8ngfhahCQrMi+sKqiqOWC/D0FVSclfugWw5/EBxJJSBSPY
umLJelbk3PqXtVm46mvGnlQlfHGppYQ5Nj6zKilCgoJoAHURp4dAaOwAhVOtc31M
0WPre/9th4TodZu1GeqFt5HqmMeP5+p2GDNDSxDNP3lgCgSqwRzy4pYaE5e70azn
Os5iaclAAlrLN3aSK6lDT+BGQbQrQgM47x3flMfszqNeuaBwdiNyzEodhT8eiYl8
m08B1sVI811q7pG8WUVZjIYs2BzVjK/VDnvbEyayX+RGCAXr5M/bfDM1YRfVS9TW
xspQNB3pyxgLX//LJIATbutUVKcrjxjoS7GdEocSruTOm/J+ZMTm0EqhsbRHVm+7
GcsSLaiSACEq+ONQJsN1x1o2GGVEi3pEZnDITSsokdcyrBj1A9a5Hx4tSiMbg88g
S4MPOALzBTmEVneQC+U5HTG/erUpZk6/AhT9Flk3HQTNIdzz8a2Clfeh4GU1JoKB
4u++XPt4M5h75OWnd5r9HYUCAwEAAQ==
-----END rsa public key-----

View File

@@ -30,6 +30,7 @@ package datetime
import (
"fmt"
"runtime"
"strings"
"time"
)
@@ -382,11 +383,110 @@ func TimestampNano(timezone ...string) int64 {
return t.UnixNano()
}
// TraceFuncTime: trace the func costed time,just call it at top of the func like `defer TraceFuncTime()()`
func TraceFuncTime() func() {
pre := time.Now()
// TrackFuncTime track the time of function execution.
// call it at top of the func like `defer TrackFuncTime(time.Now())()`
// Play: https://go.dev/play/p/QBSEdfXHPTp
func TrackFuncTime(pre time.Time) func() {
callerName := getCallerName()
return func() {
elapsed := time.Since(pre)
fmt.Println("Costs Time:\t", elapsed)
fmt.Printf("Function %s execution time:\t %v", callerName, elapsed)
}
}
func getCallerName() string {
pc, _, _, ok := runtime.Caller(2)
if !ok {
return "Unknown"
}
fn := runtime.FuncForPC(pc)
if fn == nil {
return "Unknown"
}
fullName := fn.Name()
if lastDot := strings.LastIndex(fullName, "."); lastDot != -1 {
return fullName[lastDot+1:]
}
return fullName
}
// DaysBetween returns the number of days between two times.
// Play: https://go.dev/play/p/qD6qGb3TbOy
func DaysBetween(start, end time.Time) int {
duration := end.Sub(start)
days := int(duration.Hours() / 24)
return days
}
// GenerateDatetimesBetween returns a slice of strings between two times.
// layout: the format of the datetime string
// interval: the interval between two datetimes
// Play: https://go.dev/play/p/6kHBpAxD9ZC
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error) {
var result []string
if start.After(end) {
start, end = end, start
}
duration, err := time.ParseDuration(interval)
if err != nil {
return nil, err
}
for current := start; !current.After(end); current = current.Add(duration) {
result = append(result, current.Format(layout))
}
return result, nil
}
// Min returns the earliest time among the given times.
// Play: todo
func Min(t1 time.Time, times ...time.Time) time.Time {
minTime := t1
for _, t := range times {
if t.Before(minTime) {
minTime = t
}
}
return minTime
}
// Max returns the latest time among the given times.
// Play: todo
func Max(t1 time.Time, times ...time.Time) time.Time {
maxTime := t1
for _, t := range times {
if t.After(maxTime) {
maxTime = t
}
}
return maxTime
}
// MaxMin returns the latest and earliest time among the given times.
// Play: todo
func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime time.Time) {
maxTime = t1
minTime = t1
for _, t := range times {
if t.Before(minTime) {
minTime = t
}
if t.After(maxTime) {
maxTime = t
}
}
return maxTime, minTime
}

View File

@@ -408,3 +408,61 @@ func ExampleIsWeekend() {
// true
// false
}
func ExampleDaysBetween() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
result := DaysBetween(start, end)
fmt.Println(result)
// Output:
// 9
}
func ExampleGenerateDatetimesBetween() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
layout := "2006-01-02 15:04:05"
interval := "1h"
result, err := GenerateDatetimesBetween(start, end, layout, interval)
fmt.Println(result)
fmt.Println(err)
// Output:
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
// <nil>
}
func ExampleMin() {
result := Min(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC))
fmt.Println(result)
// Output:
// 2024-09-01 00:00:00 +0000 UTC
}
func ExampleMax() {
result := Max(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC))
fmt.Println(result)
// Output:
// 2024-09-02 00:00:00 +0000 UTC
}
func ExampleMaxMin() {
max, min := MaxMin(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 3, 0, 0, 0, 0, time.UTC))
fmt.Println(max)
fmt.Println(min)
// Output:
// 2024-09-03 00:00:00 +0000 UTC
// 2024-09-01 00:00:00 +0000 UTC
}

View File

@@ -410,3 +410,171 @@ func TestTimestamp(t *testing.T) {
ts4 := TimestampNano()
t.Log(ts4)
}
func TestTrackFuncTime(t *testing.T) {
defer TrackFuncTime(time.Now())()
var n int
for i := 0; i < 5000000; i++ {
n++
}
}
func TestDaysBetween(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDaysBetween")
tests := []struct {
start time.Time
end time.Time
expected int
}{
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC),
expected: 9,
},
{
start: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
expected: -9,
},
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
expected: 0,
},
{
start: time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.December, 31, 0, 0, 0, 0, time.UTC),
expected: 365,
},
{
start: time.Date(2024, time.March, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.March, 31, 0, 0, 0, 0, time.UTC),
expected: 30,
},
}
for _, tt := range tests {
result := DaysBetween(tt.start, tt.end)
assert.Equal(tt.expected, result)
}
}
func TestGenerateDatetimesBetween(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGenerateDatetimesBetween")
tests := []struct {
start time.Time
end time.Time
layout string
interval string
expected []string
}{
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC),
layout: "2006-01-02 15:04:05",
interval: "30m",
expected: []string{
"2024-09-01 00:00:00",
"2024-09-01 00:30:00",
"2024-09-01 01:00:00",
"2024-09-01 01:30:00",
"2024-09-01 02:00:00",
},
},
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
layout: "2006-01-02 15:04:05",
interval: "1h",
expected: []string{"2024-09-01 00:00:00"},
},
{
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
end: time.Date(2024, time.September, 1, 3, 0, 0, 0, time.UTC),
layout: "2006-01-02 15:04:05",
interval: "2h",
expected: []string{
"2024-09-01 00:00:00",
"2024-09-01 02:00:00",
},
},
}
for _, tt := range tests {
result, err := GenerateDatetimesBetween(tt.start, tt.end, tt.layout, tt.interval)
assert.Equal(tt.expected, result)
assert.IsNil(err)
}
t.Run("Invalid interval", func(t *testing.T) {
_, err := GenerateDatetimesBetween(time.Now(), time.Now(), "2006-01-02 15:04:05", "invalid")
if err == nil {
t.Fatal("Expected error, got nil")
}
})
}
func TestMin(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMin")
zeroTime := time.Time{}
now := time.Now()
oneMinuteAgo := now.Add(-time.Minute)
oneMinuteAfter := now.Add(time.Minute)
assert.Equal(zeroTime, Min(zeroTime, now, oneMinuteAgo, oneMinuteAfter))
assert.Equal(zeroTime, Min(now, zeroTime))
assert.Equal(oneMinuteAgo, Min(oneMinuteAgo, now, oneMinuteAfter))
}
func TestMax(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMax")
zeroTime := time.Time{}
now := time.Now()
oneMinuteAgo := now.Add(-time.Minute)
oneMinuteAfter := now.Add(time.Minute)
assert.Equal(oneMinuteAfter, Max(zeroTime, now, oneMinuteAgo, oneMinuteAfter))
assert.Equal(now, Max(now, zeroTime))
assert.Equal(oneMinuteAfter, Max(oneMinuteAgo, now, oneMinuteAfter))
}
func TestMaxMin(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMinMax")
zeroTime := time.Time{}
now := time.Now()
oneMinuteAgo := now.Add(-time.Minute)
oneMinuteAfter := now.Add(time.Minute)
max, min := MaxMin(zeroTime, now, oneMinuteAgo, oneMinuteAfter)
assert.Equal(zeroTime, min)
assert.Equal(oneMinuteAfter, max)
max, min = MaxMin(now, zeroTime)
assert.Equal(zeroTime, min)
assert.Equal(now, max)
max, min = MaxMin(oneMinuteAgo, now, oneMinuteAfter)
assert.Equal(oneMinuteAgo, min)
assert.Equal(oneMinuteAfter, max)
}

View File

@@ -84,7 +84,7 @@ export const commonConfig = defineConfig({
footer: {
copyright: 'Copyright © 2023-present Duke Du',
message: '备案号: 京ICP备2023022770号',
message: '<a href="https://beian.miit.gov.cn/" target="_blank">京ICP备2023022770号-1</a>',
},
},
})

View File

@@ -42,7 +42,7 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: 'Contribution',
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTING.en-US.md',
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTION.md',
},
],
},

View File

@@ -51,7 +51,7 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
},
{
text: '参与贡献',
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTING.zh-CN.md',
link: 'https://github.com/duke-git/lancet/blob/main/CONTRIBUTION.zh-CN.md',
},
],
},

View File

@@ -27,7 +27,8 @@ import (
- [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand)
- [TernaryOperator](#TernaryOperator)
- [Ternary](#Ternary)
- [TernaryOperator<sup>deprecated</sup>](#TernaryOperator)
<div STYLE="page-break-after: always;"></div>
@@ -257,9 +258,45 @@ func main() {
}
```
### <span id="Ternary">Ternary</span>
<p>三元运算符。</p>
<b>函数签名:</b>
```go
func Ternary[T, U any](isTrue T, ifValue U, elseValue U) U
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ElllPZY0guT)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
conditionTrue := 2 > 1
result1 := condition.Ternary(conditionTrue, 0, 1)
conditionFalse := 2 > 3
result2 := condition.Ternary(conditionFalse, 0, 1)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 0
// 1
}
```
### <span id="TernaryOperator">TernaryOperator</span>
<p>三元运算符</p>
> ⚠️ 本函数已弃用,使用`Ternary`代替。
<b>函数签名:</b>
```go

View File

@@ -32,6 +32,8 @@ import (
- [AesCfbDecrypt](#AesCfbDecrypt)
- [AesOfbEncrypt](#AesOfbEncrypt)
- [AesOfbDecrypt](#AesOfbDecrypt)
- [AesGcmEncrypt](#AesGcmEncrypt)
- [AesGcmDecrypt](#AesGcmDecrypt)
- [Base64StdEncode](#Base64StdEncode)
- [Base64StdDecode](#Base64StdDecode)
- [DesEcbEncrypt](#DesEcbEncrypt)
@@ -68,6 +70,9 @@ import (
- [GenerateRsaKeyPair](#GenerateRsaKeyPair)
- [RsaEncryptOAEP](#RsaEncryptOAEP)
- [RsaDecryptOAEP](#RsaDecryptOAEP)
- [RsaSign](#RsaSign)
- [RsaVerifySign](#RsaVerifySign)
<div STYLE="page-break-after: always;"></div>
@@ -379,6 +384,74 @@ func main() {
}
```
### <span id="AesGcmEncrypt">AesGcmEncrypt</span>
<p>使用AES GCM算法模式加密数据。</p>
<b>函数签名:</b>
```go
func AesGcmEncrypt(data, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/rUt0-DmsPCs)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="AesGcmDecrypt">AesGcmDecrypt</span>
<p>使用AES GCM算法解密数据。</p>
<b>函数签名:</b>
```go
func AesGcmDecrypt(data, key []byte) []byte
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/rUt0-DmsPCs)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="Base64StdEncode">Base64StdEncode</span>
<p>将字符串base64编码。</p>
@@ -1537,3 +1610,81 @@ func main() {
// hello world
}
```
### <span id="RsaSign">RsaSign</span>
<p>应用RSA算法签名数据。</p>
<b>函数签名:</b>
```go
func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := []byte("This is a test data for RSA signing")
hash := crypto.SHA256
privateKey := "./rsa_private.pem"
publicKey := "./rsa_public.pem"
signature, err := RsaSign(hash, data, privateKey)
if err != nil {
return
}
err = RsaVerifySign(hash, data, signature, publicKey)
if err != nil {
return
}
}
```
### <span id="RsaVerifySign">RsaVerifySign</span>
<p>验证数据的签名是否符合RSA算法。</p>
<b>函数签名:</b>
```go
func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := []byte("This is a test data for RSA signing")
hash := crypto.SHA256
privateKey := "./rsa_private.pem"
publicKey := "./rsa_public.pem"
signature, err := RsaSign(hash, data, privateKey)
if err != nil {
return
}
err = RsaVerifySign(hash, data, signature, publicKey)
if err != nil {
return
}
}
```

View File

@@ -24,7 +24,7 @@ import (
- [New](#New)
- [FromSlice](#FromSlice)
- [Values](#Values)
- [Values<sup>deprecated</sup>](#Values)
- [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
- [AddIfNotExistBy](#AddIfNotExistBy)
@@ -101,10 +101,11 @@ func main() {
}
```
### <span id="Values">Values<sup>deprecated</sup></span>
### <span id="Values">Values</span>
<p>获取集合中所有元素的切片<br>
<a href='#ToSlice'>ToSlice()</a> 方法提供与 Values 方法相同的功能</p>
<p>获取集合中所有元素的切片。</p>
> ⚠️ 本函数已弃用,使用`ToSlice`代替。
<b>函数签名:</b>

View File

@@ -64,6 +64,12 @@ import (
- [TimestampMilli](#TimestampMilli)
- [TimestampMicro](#TimestampMicro)
- [TimestampNano](#TimestampNano)
- [TrackFuncTime](#TrackFuncTime)
- [DaysBetween](#DaysBetween)
- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
- [Min](#Min)
- [Max](#Max)
- [MaxMin](#MaxMin)
<div STYLE="page-break-after: always;"></div>
@@ -1334,7 +1340,7 @@ import (
func main() {
result1 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss")
result2 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss", "EST")
result2 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss", "EST")
fmt.Println(result1)
fmt.Println(result2)
@@ -1464,4 +1470,199 @@ func main() {
// Output:
// 1690363051331788000
}
```
### <span id="TrackFuncTime">TrackFuncTime</span>
<p>测试函数执行时间。</p>
<b>函数签名:</b>
```go
func TrackFuncTime(pre time.Time) func()
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/QBSEdfXHPTp)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
defer datetime.TrackFuncTime(time.Now())()
var n int
for i := 0; i < 5000000; i++ {
n++
}
fmt.Println(1) // Function main execution time: 1.460287ms
}
```
### <span id="DaysBetween">DaysBetween</span>
<p>返回两个日期之间的天数差。</p>
<b>函数签名:</b>
```go
func DaysBetween(start, end time.Time) int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/qD6qGb3TbOy)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
result := datetime.DaysBetween(start, end)
fmt.Println(result)
// Output:
// 9
}
```
### <span id="GenerateDatetimesBetween">GenerateDatetimesBetween</span>
<p>生成从start到end的所有日期时间的字符串列表。layout参数表示时间格式例如"2006-01-02 15:04:05"interval参数表示时间间隔例如"1h"表示1小时"30m"表示30分钟。</p>
<b>函数签名:</b>
```go
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/6kHBpAxD9ZC)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
layout := "2006-01-02 15:04:05"
interval := "1h"
result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval)
fmt.Println(result)
fmt.Println(err)
// Output:
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
// <nil>
}
```
### <span id="Min">Min</span>
<p>返回最早时间。</p>
<b>函数签名:</b>
```go
func Min(t1 time.Time, times ...time.Time) time.Time
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
minTime := datetime.Min(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC))
fmt.Println(minTime)
// Output:
// 2024-09-01 00:00:00 +0000 UTC
}
```
### <span id="Max">Max</span>
<p>返回最晚时间。</p>
<b>函数签名:</b>
```go
func Max(t1 time.Time, times ...time.Time) time.Time
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
maxTime := datetime.Min(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC))
fmt.Println(maxTime)
// Output:
// 2024-09-02 00:00:00 +0000 UTC
}
```
### <span id="MaxMin">MaxMin</span>
<p>返回最早和最晚时间。</p>
<b>函数签名:</b>
```go
func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime time.Time)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
max, min := datetime.MaxMin(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 3, 0, 0, 0, 0, time.UTC))
fmt.Println(max)
fmt.Println(min)
// Output:
// 2024-09-03 00:00:00 +0000 UTC
// 2024-09-01 00:00:00 +0000 UTC
}
```

View File

@@ -52,6 +52,7 @@ import (
- [ReadFile](#ReadFile)
- [ChunkRead](#ChunkRead)
- [ParallelChunkRead](#ParallelChunkRead)
- [GetExeOrDllVersion](#GetExeOrDllVersion)
<div STYLE="page-break-after: always;"></div>
@@ -559,7 +560,7 @@ import (
)
func main() {
err := fileutil.Zip("./test.zip", "./unzip/test.txt")
err := fileutil.UnZip("./test.zip", "./test.txt")
if err != nil {
fmt.Println(err)
}
@@ -1076,4 +1077,34 @@ func main() {
// Jim,21,male
// 2
}
```
### <span id="GetExeOrDllVersion">GetExeOrDllVersion</span>
<p>返回exe,dll文件版本号(仅Window平台).</p>
<b>函数签名:</b>
```go
func GetExeOrDllVersion(filePath string) (string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
v, err := fileutil.GetExeOrDllVersion(`C:\Program Files\Tencent\WeChat\WeChat.exe`)
if err != nil {
panic(err)
}
fmt.Println(v)
// Output:
// 3.9.10.19
}
```

View File

@@ -37,12 +37,12 @@ import (
### <span id="Comma">Comma</span>
<p>用逗号每隔3位分割数字/字符串,支持前缀添加符号。参数value必须是数字或者可以转为数字的字符串, 否则返回空字符串</p>
<p>用逗号每隔3位分割数字/字符串,支持添加前缀符号。参数value必须是数字或者可以转为数字的字符串, 否则返回空字符串</p>
<b>函数签名:</b>
```go
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string
func Comma[T constraints.Float | constraints.Integer | string](value T, prefixSymbol string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/eRD5k2vzUVX)</span></b>

View File

@@ -28,7 +28,8 @@ import (
- [Before](#Before)
- [CurryFn](#CurryFn)
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Debounce](#Debounce)
- [Debounced<sup>deprecated</sup>](#Debounced)
- [Delay](#Delay)
- [Schedule](#Schedule)
- [Pipeline](#Pipeline)
@@ -40,6 +41,7 @@ import (
- [Xnor](#Xnor)
- [Nand](#Nand)
- [AcceptIf](#AcceptIf)
- [Throttle](#Throttle)
<div STYLE="page-break-after: always;"></div>
@@ -193,9 +195,58 @@ func main() {
}
```
### <span id="Debounce">Debounce</span>
<p>创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。</p>
<b>函数签名:</b>
```go
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/-dGFrYn_1Zi)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
```
### <span id="Debounced">Debounced</span>
<p>创建一个debounced函数该函数延迟调用fn直到自上次调用debounced函数后等待持续时间过去。</p>
<p>创建一个函数的去抖动版本。</p>
> ⚠️ 本函数已弃用. 使用 `Debounce` 代替.
<b>函数签名:</b>
@@ -690,4 +741,46 @@ func main() {
// false
}
```
### <span id="Throttle">Throttle</span>
<p>创建一个函数的节流版本。返回的函数保证在每个时间间隔内最多只会被调用一次。</p>
<b>函数签名:</b>
```go
func Throttle(fn func(), interval time.Duration) func()
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/HpoMov-tJSN)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
throttledFn := function.Throttle(fn, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
}
```

View File

@@ -7,6 +7,8 @@ maputil 包包括一些操作 map 的函数。
## 源码:
- [https://github.com/duke-git/lancet/blob/main/maputil/map.go](https://github.com/duke-git/lancet/blob/main/maputil/map.go)
- [https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go](https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go)
- [https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go](https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go)
<div STYLE="page-break-after: always;"></div>
@@ -47,6 +49,24 @@ import (
- [MapToStruct](#MapToStruct)
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
- [NewOrderedMap](#NewOrderedMap)
- [OrderedMap_Set](#OrderedMap_Set)
- [OrderedMap_Get](#OrderedMap_Get)
- [OrderedMap_Front](#OrderedMap_Front)
- [OrderedMap_Back](#OrderedMap_Back)
- [OrderedMap_Delete](#OrderedMap_Delete)
- [OrderedMap_Clear](#OrderedMap_Clear)
- [OrderedMap_Len](#OrderedMap_Len)
- [OrderedMap_Keys](#OrderedMap_Keys)
- [OrderedMap_Values](#OrderedMap_Values)
- [OrderedMap_Contains](#OrderedMap_Contains)
- [OrderedMap_Range](#OrderedMap_Range)
- [OrderedMap_Elements](#OrderedMap_Elements)
- [OrderedMap_Iter](#OrderedMap_Iter)
- [OrderedMap_ReverseIter](#OrderedMap_ReverseIter)
- [OrderedMap_SortByKey](#OrderedMap_SortByKey)
- [OrderedMap_MarshalJSON](#OrderedMap_MarshalJSON)
- [OrderedMap_UnmarshalJSON](#OrderedMap_UnmarshalJSON)
- [NewConcurrentMap](#NewConcurrentMap)
- [ConcurrentMap_Get](#ConcurrentMap_Get)
- [ConcurrentMap_Set](#ConcurrentMap_Set)
@@ -56,6 +76,8 @@ import (
- [ConcurrentMap_Has](#ConcurrentMap_Has)
- [ConcurrentMap_Range](#ConcurrentMap_Range)
- [GetOrSet](#GetOrSet)
- [SortByKey](#SortByKey)
- [GetOrDefault](#GetOrDefault)
<div STYLE="page-break-after: always;"></div>
@@ -1120,6 +1142,690 @@ func main() {
}
```
### <span id="NewOrderedMap">NewOrderedMap</span>
<p>创建有序映射。有序映射是键值对的集合,其中键是唯一的,并且保留键插入的顺序。</p>
<b>函数签名:</b>
```go
func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V]
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Set">OrderedMap_Set</span>
<p>设置给定的键值对。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Set(key K, value V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Y4ZJ_oOc1FU)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Get">OrderedMap_Get</span>
<p>返回给定键的值。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Get(key K) (V, bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Y4ZJ_oOc1FU)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Delete">OrderedMap_Delete</span>
<p>删除给定键的键值对。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Delete(key K)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/5bIi4yaZ3K-)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Delete("b")
fmt.Println(om.Keys())
// Output:
// [a c]
}
```
### <span id="OrderedMap_Clear">OrderedMap_Clear</span>
<p>清空map数据。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Clear()
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/8LwoJyEfuFr)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Clear()
fmt.Println(om.Keys())
// Output:
// []
}
```
### <span id="OrderedMap_Front">OrderedMap_Front</span>
<p>返回第一个键值对。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Front() (struct {
Key K
Value V
}, bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ty57XSimpoe)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
frontElement, ok := om.Front()
fmt.Println(frontElement)
fmt.Println(ok)
// Output:
// {a 1}
// true
}
```
### <span id="OrderedMap_Back">OrderedMap_Back</span>
<p>返回最后一个键值对。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Back() (struct {
Key K
Value V
}, bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/rQMjp1yQmpa)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
backElement, ok := om.Back()
fmt.Println(backElement)
fmt.Println(ok)
// Output:
// {c 3}
// true
}
```
### <span id="OrderedMap_Range">OrderedMap_Range</span>
<p>为每个键值对调用给定的函数。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/U-KpORhc7LZ)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Range(func(key string, value int) bool {
fmt.Println(key, value)
return true
})
// Output:
// a 1
// b 2
// c 3
}
```
### <span id="OrderedMap_Keys">OrderedMap_Keys</span>
<p>按顺序返回键的切片。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Keys() []K
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Vv_y9ExKclA)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
keys := om.Keys()
fmt.Println(keys)
// Output:
// [a b c]
}
```
### <span id="OrderedMap_Values">OrderedMap_Values</span>
<p>按顺序返回值的切片。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Values() []V
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/TWj5n1-PUfx)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
values := om.Values()
fmt.Println(values)
// Output:
// [1 2 3]
}
```
### <span id="OrderedMap_Elements">OrderedMap_Elements</span>
<p>按顺序返回键值对。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Elements() []struct
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/4BHG4kKz6bB)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
elements := om.Elements()
fmt.Println(elements)
// Output:
// [{a 1} {b 2} {c 3}]
}
```
### <span id="OrderedMap_Len">OrderedMap_Len</span>
<p>返回键值对的数量。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Len() int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/cLe6z2VX5N-)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Len()
fmt.Println(om.Len())
// Output:
// 3
}
```
### <span id="OrderedMap_Contains">OrderedMap_Contains</span>
<p>如果给定的键存在则返回true。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Contains(key K) bool
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/QuwqqnzwDNX)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
result1 := om.Contains("a")
result2 := om.Contains("d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="OrderedMap_Iter">OrderedMap_Iter</span>
<p>返回按顺序产生键值对的通道。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) Iter() <-chan struct {
Key K
Value V
}
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/tlq2tdvicPt)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.Iter() {
fmt.Println(elem)
}
// Output:
// {a 1}
// {b 2}
// {c 3}
}
```
### <span id="OrderedMap_ReverseIter">OrderedMap_ReverseIter</span>
<p>返回以相反顺序产生键值对的通道。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) ReverseIter() <-chan struct {
Key K
Value V
}
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/8Q0ssg6hZzO)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.ReverseIter() {
fmt.Println(elem)
}
// Output:
// {c 3}
// {b 2}
// {a 1}
}
```
### <span id="OrderedMap_SortByKey">OrderedMap_SortByKey</span>
<p>使用传入的比较函数排序map key。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/N7hjD_alZPq)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[int, string]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
om.SortByKey(func(a, b int) bool {
return a < b
})
fmt.Println(om.Elements())
// Output:
// [{1 a} {2 b} {3 c} {4 d}]
}
```
### <span id="OrderedMap_MarshalJSON">OrderedMap_MarshalJSON</span>
<p>实现json.Marshaler接口。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/C-wAwydIAC7)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[int, string]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
b, _ := om.MarshalJSON()
fmt.Println(string(b))
// Output:
// {"a":1,"b":2,"c":3}
}
```
### <span id="OrderedMap_UnmarshalJSON">OrderedMap_UnmarshalJSON</span>
<p>实现json.Unmarshaler接口。</p>
<b>函数签名:</b>
```go
func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/8C3MvJ3-mut)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
data := []byte(`{"a":1,"b":2,"c":3}`)
om.UnmarshalJSON(data)
fmt.Println(om.Elements())
// Output:
// [{a 1} {b 2} {c 3}]
}
```
### <span id="NewConcurrentMap">NewConcurrentMap</span>
<p>ConcurrentMap协程安全的map结构。</p>
@@ -1497,7 +2203,7 @@ func main() {
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V
```
<b>示例:<span style="float:right;display:inline-block;"></span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/IVQwO1OkEJC)</span></b>
```go
package main
@@ -1522,4 +2228,83 @@ func main() {
// a
// b
}
```
### <span id="SortByKey">SortByKey</span>
<p>对传入的map根据key进行排序返回排序后的map。</p>
<b>函数签名:</b>
```go
func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/PVdmBSnm6P_W)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result := maputil.SortByKey(m, func(a, b int) bool {
return a < b
})
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
```
### <span id="GetOrDefault">GetOrDefault</span>
<p>返回给定键的值,如果键不存在,则返回默认值。</p>
<b>函数签名:</b>
```go
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/99QjSYSBdiM)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result1 := maputil.GetOrDefault(m, 1, "default")
result2 := maputil.GetOrDefault(m, 6, "default")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// a
// default
}
```

View File

@@ -33,7 +33,7 @@ import (
- [Percent](#Percent)
- [RoundToFloat](#RoundToFloat)
- [RoundToString](#RoundToString)
- [TruncRound](#TruncRound)
- [T运行cRound](#T运行cRound)
- [CeilToFloat](#CeilToFloat)
- [CeilToString](#CeilToString)
- [FloorToFloat](#FloorToFloat)
@@ -52,6 +52,10 @@ import (
- [Sum](#Sum)
- [Abs](#Abs)
- [Div](#Div)
- [Variance](#Variance)
- [StdDev](#StdDev)
- [Permutation](#Permutation)
- [Combination](#Combination)
<div STYLE="page-break-after: always;"></div>
@@ -64,7 +68,7 @@ import (
<b>函数签名:</b>
```go
func Average[T constraints.Integer | constraints.Float](numbers ...T) T
func Average[T constraints.Integer | constraints.Float](numbers ...T) float64
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/HFd70x4DrMj)</span></b>
@@ -87,7 +91,7 @@ func main() {
fmt.Println(result2)
// Output:
// 1
// 1.5
// 1.3
}
```
@@ -462,14 +466,14 @@ func main() {
}
```
### <span id="TruncRound">TruncRound</span>
### <span id="T运行cRound">T运行cRound</span>
<p>截短n位小数不进行四舍五入</p>
<b>函数签名:</b>
```go
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T
func T运行cRound[T constraints.Float | constraints.Integer](x T, n int) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/aumarSHIGzP)</span></b>
@@ -483,9 +487,9 @@ import (
)
func main() {
result1 := mathutil.TruncRound(0.124, 2)
result2 := mathutil.TruncRound(0.125, 2)
result3 := mathutil.TruncRound(0.125, 3)
result1 := mathutil.T运行cRound(0.124, 2)
result2 := mathutil.T运行cRound(0.125, 2)
result3 := mathutil.T运行cRound(0.125, 3)
fmt.Println(result1)
fmt.Println(result2)
@@ -1045,8 +1049,8 @@ import (
func main() {
result1 := mathutil.Log(8, 2)
result2 := mathutil.TruncRound(mathutil.Log(5, 2), 2)
result3 := mathutil.TruncRound(mathutil.Log(27, 3), 0)
result2 := mathutil.T运行cRound(mathutil.Log(5, 2), 2)
result3 := mathutil.T运行cRound(mathutil.Log(27, 3), 0)
fmt.Println(result1)
fmt.Println(result2)
@@ -1162,4 +1166,136 @@ func main() {
// 0.5
// 0
}
```
### <span id="Variance">Variance</span>
<p>计算方差。</p>
<b>函数签名:</b>
```go
func Variance[T constraints.Float | constraints.Integer](numbers []T) float64
```
<b>示例:<span style="float:right;display:inline-block;">[示例](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Variance([]int{1, 2, 3, 4, 5})
result2 := mathutil.Variance([]float64{1.1, 2.2, 3.3, 4.4, 5.5})
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 2
// 2.42
}
```
### <span id="StdDev">StdDev</span>
<p>计算标准差。</p>
<b>函数签名:</b>
```go
func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.T运行cRound(mathutil.StdDev([]int{1, 2, 3, 4, 5}), 2)
result2 := mathutil.T运行cRound(mathutil.StdDev([]float64{1.1, 2.2, 3.3, 4.4, 5.5}), 2)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 1.41
// 1.55
}
```
### <span id="Permutation">Permutation</span>
<p>计算排列数P(n, k))。</p>
<b>函数签名:</b>
```go
func Permutation(n, k uint) uint
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Permutation(5, 3)
result2 := mathutil.Permutation(5, 5)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 60
// 120
}
```
### <span id="Combination">Combination</span>
<p>计算组合数C(n, k))。</p>
<b>函数签名:</b>
```go
func Combination(n, k uint) uint
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Combination(5, 3)
result2 := mathutil.Combination(5, 5)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 10
// 1
}
```

View File

@@ -624,7 +624,9 @@ func main() {
### <span id="HttpGet">HttpGet</span>
<p>发送http get请求。(已废弃:使用SendRequest)</p>
<p>发送http get请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -666,7 +668,9 @@ func main() {
### <span id="HttpPost">HttpPost</span>
<p>发送http post请求。(已废弃:使用SendRequest)</p>
<p>发送http post请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -713,7 +717,9 @@ func main() {
### <span id="HttpPut">HttpPut</span>
<p>发送http put请求。(已废弃:使用SendRequest)</p>
<p>发送http put请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -763,7 +769,9 @@ func main() {
### <span id="HttpDelete">HttpDelete</span>
<p>发送http delete请求。(已废弃:使用SendRequest)</p>
<p>发送http delete请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>
@@ -802,7 +810,9 @@ func main() {
### <span id="HttpPatch">HttpPatch</span>
<p>发送http patch请求。(已废弃:使用SendRequest)</p>
<p>发送http patch请求。</p>
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
<b>函数签名:</b>

View File

@@ -25,15 +25,23 @@ import (
- [RandBytes](#RandBytes)
- [RandInt](#RandInt)
- [RandString](#RandString)
- [RandFromGivenSlice](#RandFromGivenSlice)
- [RandSliceFromGivenSlice](#RandSliceFromGivenSlice)
- [RandUpper](#RandUpper)
- [RandLower](#RandLower)
- [RandNumeral](#RandNumeral)
- [RandNumeralOrLetter](#RandNumeralOrLetter)
- [RandSymbolChar](#RandSymbolChar)
- [UUIdV4](#UUIdV4)
- [RandIntSlice](#RandIntSlice)
- [RandUniqueIntSlice](#RandUniqueIntSlice)
- [RandFloat](#RandFloat)
- [RandFloats](#RandFloats)
- [RandStringSlice](#RandStringSlice)
- [RandBool](#RandBool)
- [RandBoolSlice](#RandBoolSlice)
- [RandNumberOfLength](#RandNumberOfLength)
<div STYLE="page-break-after: always;"></div>
@@ -117,6 +125,62 @@ func main() {
}
```
### <span id="RandFromGivenSlice">RandFromGivenSlice</span>
<p>从给定切片中随机生成元素。</p>
<b>函数签名:</b>
```go
func RandFromGivenSlice[T any](slice []T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/UrkWueF6yYo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
nicknames := []string{"张三", "李四", "王五", "赵六", "钱七"}
randElm := random.RandFromGivenSlice(nicknames)
fmt.Println(randElm)
}
```
### <span id="RandSliceFromGivenSlice">RandSliceFromGivenSlice</span>
<p>从给定切片中生成长度为 num 的随机切片。</p>
<b>函数签名:</b>
```go
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/68UikN9d6VT)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon","mango", "nectarine", "orange"}
chosen3goods := random.RandSliceFromGivenSlice(goods, 3, false)
fmt.Println(chosen3goods)
}
```
### <span id="RandUpper">RandUpper</span>
<p>生成给定长度的随机大写字母字符串</p>
@@ -276,14 +340,40 @@ func main() {
}
```
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
### <span id="RandIntSlice">RandIntSlice</span>
<p>生成一个不重复的长度为n的随机int切片。</p>
<p>生成一个特定长度的随机int切片数值范围[min, max)。</p>
<b>函数签名:</b>
```go
func RandUniqueIntSlice(n, min, max int) []int
func RandIntSlice(length, min, max int) []int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/GATTQ5xTEG8)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
result := random.RandIntSlice(5, 0, 10)
fmt.Println(result) //[1 2 7 1 5] (random)
}
```
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
<p>生成一个特定长度的数值不重复的随机int切片数值范围[min, max)。</p>
<b>函数签名:</b>
```go
func RandUniqueIntSlice(length, min, max int) []int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uBkRSOz73Ec)</span></b>
@@ -304,7 +394,7 @@ func main() {
### <span id="RandFloat">RandFloat</span>
<p>生成随机float64数,可以指定范围和精度。</p>
<p>生成一个随机float64数,可以指定精度。数值范围[min, max)。</p>
<b>函数签名:</b>
@@ -330,7 +420,7 @@ func main() {
### <span id="RandFloats">RandFloats</span>
<p>生成随机float64数字切片,指定长度,范围和精度.</p>
<p>生成一个特定长度的随机float64切片可以指定数值精度。数值范围[min, max)。</p>
<b>函数签名:</b>
@@ -352,4 +442,110 @@ func main() {
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
fmt.Println(floatNumber) //[3.42 3.99 1.3 2.38 4.23] (random)
}
```
### <span id="RandStringSlice">RandStringSlice</span>
<p>生成随机字符串slice. 字符串类型需要是以下几种或者它们的组合: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars。</p>
<b>函数签名:</b>
```go
func RandStringSlice(charset string, sliceLen, strLen int) []string
```
<b>实例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/2_-PiDv3tGn)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
strs := random.RandStringSlice(random.Letters, 4, 6)
fmt.Println(strs)
// output random string slice like below:
//[CooSMq RUFjDz FAeMPf heRyGv]
}
```
### <span id="RandBool">RandBool</span>
<p>生成随机bool值(true or false)。</p>
<b>函数签名:</b>
```go
func RandBool() bool
```
<b>实例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/to6BLc26wBv)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
result := random.RandBool()
fmt.Println(result) // true or false (random)
}
```
### <span id="RandBoolSlice">RandBoolSlice</span>
<p>生成特定长度的随机bool slice。</p>
<b>函数签名:</b>
```go
func RandBoolSlice(length int) []bool
```
<b>实例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/o-VSjPjnILI)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
result := random.RandBoolSlice(2)
fmt.Println(result) // [true false] (random)
}
```
### <span id="RandNumberOfLength">RandNumberOfLength</span>
<p>生成指定长度的随机数</p>
<b>函数签名:</b>
```go
func RandNumberOfLength(len int) int
```
<b>实例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
i := random.RandNumberOfLength(2)
fmt.Println(i) // 21 (random number of length 2)
}
```

View File

@@ -70,7 +70,7 @@ func main() {
return errors.New("error occurs")
}
duration := retry.RetryDuration(time.Microsecond*50)
duration := retry.RetryWithLinearBackoff(time.Microsecond*50)
retry.Retry(increaseNumber,
duration,
@@ -116,7 +116,7 @@ func main() {
return errors.New("error occurs")
}
duration := retry.RetryDuration(time.Microsecond*50)
duration := retry.RetryWithLinearBackoff(time.Microsecond*50)
err := retry.Retry(increaseNumber, duration)
if err != nil {
@@ -173,52 +173,6 @@ func main() {
}
```
### <span id="RetryDuration">RetryDuration</span>
<p>设置重试间隔时间默认3秒</p>
<b>函数签名:</b>
```go
func RetryDuration(d time.Duration)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/nk2XRmagfVF)</span></b>
```go
package main
import (
"fmt"
"errors"
"log"
"github.com/duke-git/lancet/v2/retry"
)
func main() {
number := 0
increaseNumber := func() error {
number++
if number == 3 {
return nil
}
return errors.New("error occurs")
}
duration := retry.RetryDuration(time.Microsecond*50)
err := retry.Retry(increaseNumber, duration)
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
```
### <span id="Retry">Retry</span>
<p>重试执行函数retryFunc直到函数运行成功或被context停止</p>
@@ -251,7 +205,7 @@ func main() {
return errors.New("error occurs")
}
duration := retry.RetryDuration(time.Microsecond*50)
duration := retry.RetryWithLinearBackoff(time.Microsecond*50)
err := retry.Retry(increaseNumber, duration)
if err != nil {
@@ -331,7 +285,7 @@ func main() {
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jIm_o2vb5Y4)</span></b>
```go
package main
@@ -384,7 +338,7 @@ func main() {
func RetryWithLinearBackoff(interval time.Duration) Option
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/PDet2ZQZwcB)</span></b>
```go
package main
@@ -429,7 +383,7 @@ func main() {
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/xp1avQmn16X)</span></b>
```go
package main

View File

@@ -6,7 +6,8 @@ slice 包包含操作切片的方法集合。
## 源码:
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go](https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go)
<div STYLE="page-break-after: always;"></div>
@@ -44,6 +45,7 @@ import (
- [Equal](#Equal)
- [EqualWith](#EqualWith)
- [Filter](#Filter)
- [FilterConcurrent](#FilterConcurrent)
- [Find<sup>deprecated</sup>](#Find)
- [FindBy](#FindBy)
- [FindLast<sup>deprecated</sup>](#FindLast)
@@ -51,6 +53,7 @@ import (
- [Flatten](#Flatten)
- [FlattenDeep](#FlattenDeep)
- [ForEach](#ForEach)
- [ForEachConcurrent](#ForEachConcurrent)
- [ForEachWithBreak](#ForEachWithBreak)
- [GroupBy](#GroupBy)
- [GroupWith](#GroupWith)
@@ -61,11 +64,13 @@ import (
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [Map](#Map)
- [MapConcurrent](#MapConcurrent)
- [FilterMap](#FilterMap)
- [FlatMap](#FlatMap)
- [Merge](#Merge)
- [Reverse](#Reverse)
- [Reduce<sup>deprecated</sup>](#Reduce)
- [ReduceConcurrent](#ReduceConcurrent)
- [ReduceBy](#ReduceBy)
- [ReduceRight](#ReduceRight)
- [Replace](#Replace)
@@ -86,7 +91,9 @@ import (
- [ToSlicePointer](#ToSlicePointer)
- [Unique](#Unique)
- [UniqueBy](#UniqueBy)
- [UniqueByComparator](#UniqueByComparator)
- [UniqueByField](#UniqueByField)
- [UniqueByConcurrent](#UniqueByConcurrent)
- [Union](#Union)
- [UnionBy](#UnionBy)
- [UpdateAt](#UpdateAt)
@@ -98,6 +105,10 @@ import (
- [Break](#Break)
- [RightPadding](#RightPadding)
- [LeftPadding](#LeftPadding)
- [Frequency](#Frequency)
- [JoinFunc](#JoinFunc)
- [ConcatBy](#ConcatBy)
<div STYLE="page-break-after: always;"></div>
@@ -897,10 +908,46 @@ func main() {
}
```
### <span id="Find">Find (废弃:使用 FindBy)</span>
### <span id="FilterConcurrent">FilterConcurrent</span>
<p>对slice并发执行filter操作。</p>
<b>函数签名:</b>
```go
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/t_pkwerIRVx)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
result := slice.FilterConcurrent(nums, isEven, 2)
fmt.Println(result)
// Output:
// [2 4]
}
```
### <span id="Find">Find</span>
<p>遍历slice的元素返回第一个通过predicate函数真值测试的元素</p>
> ⚠️ 本函数已弃用,使用`FindBy`代替。
<b>函数签名:</b>
```go
@@ -969,10 +1016,12 @@ func main() {
}
```
### <span id="FindLast">FindLast(废弃:使用 FindLastBy)</span>
### <span id="FindLast">FindLast</span>
<p>遍历slice的元素返回最后一个通过predicate函数真值测试的元素。</p>
> ⚠️ 本函数已弃用,使用`FindLastBy`代替。
<b>函数签名:</b>
```go
@@ -1136,6 +1185,43 @@ func main() {
}
```
### <span id="ForEachConcurrent">ForEachConcurrent</span>
<p>对slice并发执行foreach操作。</p>
<b>函数签名:</b>
```go
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/kT4XW7DKVoV)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
result := make([]int, len(nums))
addOne := func(index int, value int) {
result[index] = value + 1
}
slice.ForEachConcurrent(nums, addOne, 4)
fmt.Println(result)
// Output:
// [2 3 4 5 6 7 8 9]
}
```
### <span id="ForEachWithBreak">ForEachWithBreak</span>
<p>遍历切片的元素并为每个元素调用iteratee函数当iteratee函数返回false时终止遍历。</p>
@@ -1244,10 +1330,12 @@ func main() {
}
```
### <span id="IntSlice">IntSlice (已弃用: 使用 go1.18+泛型代替)</span>
### <span id="IntSlice">IntSlice</span>
<p>将接口切片转换为int切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -1273,10 +1361,12 @@ func main() {
}
```
### <span id="InterfaceSlice">InterfaceSlice(已弃用: 使用 go1.18+泛型代替)</span>
### <span id="InterfaceSlice">InterfaceSlice</span>
<p>将值转换为接口切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -1441,7 +1531,7 @@ func main() {
### <span id="Map">Map</span>
<p>对slice中的每个元素执行map函数以创建一个新切片</p>
<p>对slice中的每个元素执行map函数以创建一个新切片</p>
<b>函数签名:</b>
@@ -1473,6 +1563,38 @@ func main() {
}
```
### <span id="MapConcurrent">MapConcurrent</span>
<p>对slice并发执行map操作。</p>
<b>函数签名:</b>
```go
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/H1ehfPkPen0)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6}
result := slice.MapConcurrent(nums, func(_, n int) int {
return n * n
}, 4)
fmt.Println(result)
// Output:
// [1 4 9 16 25 36]
}
```
### <span id="FilterMap">FilterMap</span>
<p>返回一个将filter和map操作应用于给定切片的切片。 iteratee回调函数应该返回两个值1结果值。2结果值是否应该被包含在返回的切片中。</p>
@@ -1543,10 +1665,12 @@ func main() {
}
```
### <span id="Merge">Merge废弃使用Concat</span>
### <span id="Merge">Merge</span>
<p>合并多个切片(不会消除重复元素).</p>
> ⚠️ 本函数已弃用,使用`Concat`代替。
<b>函数签名:</b>
```go
@@ -1606,7 +1730,9 @@ func main() {
### <span id="Reduce">Reduce</span>
<p>将切片中的元素依次运行iteratee函数返回运行结果(废弃建议使用ReduceBy)</p>
<p>将切片中的元素依次运行iteratee函数返回运行结果</p>
> ⚠️ 本函数已弃用,使用`ReduceBy`代替。
<b>函数签名:</b>
@@ -1638,6 +1764,38 @@ func main() {
}
```
### <span id="ReduceConcurrent">ReduceConcurrent</span>
<p>对切片元素执行并发reduce操作。</p>
<b>函数签名:</b>
```go
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Tjwe6OtaG07)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := slice.ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 1)
fmt.Println(result)
// Output:
// 55
}
```
### <span id="ReduceBy">ReduceBy</span>
<p>对切片元素执行reduce操作。</p>
@@ -2132,10 +2290,12 @@ func main() {
}
```
### <span id="StringSlice">StringSlice(已弃用: 使用 go1.18+泛型代替)</span>
### <span id="StringSlice">StringSlice</span>
<p>将接口切片转换为字符串切片</p>
> ⚠️ 本函数已弃用使用go1.18+泛型代替。
<b>函数签名:</b>
```go
@@ -2284,15 +2444,15 @@ func main() {
### <span id="UniqueBy">UniqueBy</span>
<p>对切片的每个元素调用iteratee函数然后删除重复元素</p>
<p>根据迭代函数返回的值,从输入切片中移除重复元素。此函数保持元素的顺序。</p>
<b>函数签名:</b>
```go
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/UR323iZLDpv)</span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/GY7JE4yikrl)</span></b>
```go
import (
@@ -2309,7 +2469,73 @@ func main() {
fmt.Println(result)
// Output:
// [1 2 0]
// [1 2 3]
}
```
### <span id="UniqueByComparator">UniqueByComparator</span>
<p>使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。</p>
<b>函数签名:</b>
```go
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/rwSacr-ZHsR)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
uniqueNums := slice.UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
return item == other
})
caseInsensitiveStrings := slice.UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool {
return strings.ToLower(item) == strings.ToLower(other)
})
fmt.Println(uniqueNums)
fmt.Println(caseInsensitiveStrings)
// Output:
// [1 2 3 4 5 6]
// [apple banana cherry date]
}
```
### <span id="UniqueByConcurrent">UniqueByConcurrent</span>
<p>并发的从输入切片中移除重复元素,结果保持元素的顺序。</p>
<b>函数签名:</b>
```go
func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wXZ7LcYRMGL)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool { return item == other }
result := slice.UniqueByConcurrent(nums, comparator, 4)
fmt.Println(result)
// Output:
// [1 2 3 4 5 6 7]
}
```
@@ -2323,7 +2549,7 @@ func main() {
func UniqueByField[T any](slice []T, field string) ([]T, error)
```
<b>示例:<span style="float:right;display:inline-block;"></span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/6cifcZSPIGu)</span></b>
```go
import (
@@ -2680,7 +2906,7 @@ func main() {
}
```
<span id="RightPadding">RightPadding</span>
### <span id="RightPadding">RightPadding</span>
<p>在切片的右部添加元素。</p>
@@ -2699,7 +2925,7 @@ import (
)
func main() {
nums := []int{1, 2, 3, 4, 5}
nums := []int{1, 2, 3, 4, 5}
padded := slice.RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
@@ -2707,7 +2933,7 @@ func main() {
}
```
<span id="LeftPadding">LeftPadding</span>
### <span id="LeftPadding">LeftPadding</span>
<p>在切片的左部添加元素。</p>
@@ -2726,10 +2952,116 @@ import (
)
func main() {
nums := []int{1, 2, 3, 4, 5}
nums := []int{1, 2, 3, 4, 5}
padded := slice.LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
```
### <span id="Frequency">Frequency</span>
<p>计算切片中每个元素出现的频率。</p>
<b>函数签名:</b>
```go
func Frequency[T comparable](slice []T) map[T]int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/CW3UVNdUZOq)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "b", "c", "c", "c"}
result := slice.Frequency(strs)
fmt.Println(result)
// Output:
// map[a:1 b:2 c:3]
}
```
### <span id="JoinFunc">JoinFunc</span>
<p>将切片元素用给定的分隔符连接成一个单一的字符串。</p>
<b>函数签名:</b>
```go
func JoinFunc[T any](slice []T, sep string, transform func(T) T) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
result := slice.JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string {
return strings.ToUpper(s)
})
fmt.Println(result)
// Output:
// A, B, C
}
```
### <span id="ConcatBy">ConcatBy</span>
<p>将切片中的元素连接成一个值,使用指定的分隔符和连接器函数。</p>
<b>函数签名:</b>
```go
func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
type Person struct {
Name string
Age int
}
people := []Person{
{Name: "Alice", Age: 30},
{Name: "Bob", Age: 25},
{Name: "Charlie", Age: 35},
}
sep := Person{Name: " | ", Age: 0}
personConnector := func(a, b Person) Person {
return Person{Name: a.Name + b.Name, Age: a.Age + b.Age}
}
result := slice.ConcatBy(people, sep, personConnector)
fmt.Println(result.Name)
fmt.Println(result.Age)
// Output:
// Alice | Bob | Charlie
// 90
}
```

View File

@@ -1,6 +1,6 @@
# Stream
Stream 流,该包仅验证简单 stream 实现,功能有限。
Stream流该包仅验证简单stream实现功能有限。
<div STYLE="page-break-after: always;"></div>
@@ -48,6 +48,8 @@ import (
- [NoneMatch](#NoneMatch)
- [Count](#Count)
- [ToSlice](#ToSlice)
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
<div STYLE="page-break-after: always;"></div>
@@ -938,3 +940,69 @@ func main() {
// [1 2 3]
}
```
### <span id="IndexOf">IndexOf</span>
<p>返回在stream中找到值的第一个匹配项的索引如果找不到值则返回-1。</p>
<b>函数签名:</b>
```go
func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/stream"
)
func main() {
s := stream.FromSlice([]int{1, 2, 3, 2})
result1 := s.IndexOf(0, func(a, b int) bool { return a == b })
result2 := s.IndexOf(2, func(a, b int) bool { return a == b })
fmt.Println(result1)
fmt.Println(result2)
// Output:
// -1
// 1
}
```
### <span id="LastIndexOf">LastIndexOf</span>
<p>返回在stream中找到值的最后一个匹配项的索引如果找不到值则返回-1。</p>
<b>函数签名:</b>
```go
func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/stream"
)
func main() {
s := stream.FromSlice([]int{1, 2, 3, 2})
result1 := s.LastIndexOf(0, func(a, b int) bool { return a == b })
result2 := s.LastIndexOf(2, func(a, b int) bool { return a == b })
fmt.Println(result1)
fmt.Println(result2)
// Output:
// -1
// 3
}
```

View File

@@ -63,6 +63,13 @@ import (
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
- [Ellipsis](#Ellipsis)
- [Shuffle](#Shuffle)
- [Rotate](#Rotate)
- [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups)
- [ExtractContent](#ExtractContent)
<div STYLE="page-break-after: always;"></div>
@@ -1541,7 +1548,7 @@ func main() {
func Concat(length int, str ...string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gD52SZHr4Kp)</span></b>
```go
import (
@@ -1550,17 +1557,206 @@ import (
)
func main() {
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}
```
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
### <span id="Ellipsis">Ellipsis</span>
<p>将字符串截断到指定长度,并在末尾添加省略号。</p>
<b>函数签名:</b>
```go
func Ellipsis(str string, length int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/i1vbdQiQVRR)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Ellipsis("hello world", 5)
result2 := strutil.Ellipsis("你好,世界!", 2)
result3 := strutil.Ellipsis("😀😃😄😁😆", 3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// hello...
// 你好...
// 😀😃😄...
}
```
### <span id="Shuffle">Shuffle</span>
<p>打乱给定字符串中的字符顺序。</p>
<b>函数签名:</b>
```go
func Shuffle(str string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/iStFwBwyGY7)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result := strutil.Shuffle("hello")
fmt.Println(result) //olelh (random order)
}
```
### <span id="Rotate">Rotate</span>
<p>按指定的字符数旋转字符串。</p>
<b>函数签名:</b>
```go
func Rotate(str string, shift int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Kf03iOeT5bd)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Rotate("Hello", 0)
result2 := strutil.Rotate("Hello", 1)
result3 := strutil.Rotate("Hello", 2)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello
// oHell
// loHel
}
```
### <span id="TemplateReplace">TemplateReplace</span>
<p>将模板字符串中的占位符替换为map中的相应值。占位符括在花括号中例如 {key}。例如模板字符串为“Hello, {name}!”map为{"name": "world"}结果将为“Hello, world!”。</p>
<b>函数签名:</b>
```go
func TemplateReplace(template string, data map[string]string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/cXSuFvyZqv9)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
result := strutil.TemplateReplace(template, data)
fmt.Println(result)
// Output:
// Hello, my name is Bob, I'm 20 years old.
}
```
### <span id="RegexMatchAllGroups">RegexMatchAllGroups</span>
<p>使用正则表达式匹配字符串中的所有子组并返回结果。</p>
<b>函数签名:</b>
```go
func RegexMatchAllGroups(pattern, str string) [][]string
```
<b>示例:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/JZiu0RXpgN-)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
str := "Emails: john.doe@example.com and jane.doe@example.com"
result := strutil.RegexMatchAllGroups(pattern, str)
fmt.Println(result[0])
fmt.Println(result[1])
// Output:
// [john.doe@example.com john.doe example com]
// [jane.doe@example.com jane.doe example com]
}
```
### <span id="ExtractContent">ExtractContent</span>
<p>提取两个标记之间的内容。</p>
<b>函数签名:</b>
```go
func ExtractContent(s, start, end string) []string
```
<b>示例:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
html := `<span>content1</span>aa<span>content2</span>bb<span>content1</span>`
result := strutil.ExtractContent(html, "<span>", "</span>")
fmt.Println(result)
// Output:
// [content1 content2 content1]
}
```

View File

@@ -1,6 +1,6 @@
# System
system 包含 os, runtime, shell command 相关函数。
system 包含 os, 运行time, shell command 相关函数。
<div STYLE="page-break-after: always;"></div>
@@ -31,6 +31,11 @@ import (
- [CompareOsEnv](#CompareOsEnv)
- [ExecCommand](#ExecCommand)
- [GetOsBits](#GetOsBits)
- [StartProcess](#StartProcess)
- [StopProcess](#StopProcess)
- [KillProcess](#KillProcess)
- [GetProcessInfo](#GetProcessInfo)
<div STYLE="page-break-after: always;"></div>
@@ -241,13 +246,14 @@ func main() {
### <span id="ExecCommand">ExecCommand</span>
<p>执行shell命令返回命令的stdout和stderr字符串如果出现错误则返回错误。参数`command`是一个完整的命令字符串如ls-alinuxdirwindowsping 127.0.0.1。在linux中使用/bin/bash-c执行命令在windows中使用powershell.exe执行命令。</p>
<p>执行shell命令返回命令的stdout和stderr字符串如果出现错误则返回错误。参数`command`是一个完整的命令字符串如ls-alinuxdirwindowsping 127.0.0.1。在linux中使用/bin/bash-c执行命令在windows中使用powershell.exe执行命令。
函数的第二个参数是cmd选项控制参数类型是func(*exec.Cmd)可以通过这个参数设置cmd属性。</p>
<b>函数签名:</b>
```go
type (
Option func(*exec.Cmd)
Option func(*exec.Cmd)
)
func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error)
```
@@ -262,7 +268,9 @@ import (
func main() {
// linux or mac
stdout, stderr, err := system.ExecCommand("ls")
stdout, stderr, err := system.ExecCommand("ls", func(cmd *exec.Cmd) {
cmd.Dir = "/tmp"
})
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
assert.Equal("", stderr)
@@ -305,3 +313,132 @@ func main() {
fmt.Println(osBit) // 32 or 64
}
```
### <span id="StartProcess">StartProcess</span>
<p>创建进程。</p>
<b>函数签名:</b>
```go
func StartProcess(command string, args ...string) (int, error)
```
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/5GVol6ryS_X)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "2")
if err != nil {
return
}
fmt.Println(pid)
}
```
### <span id="StopProcess">StopProcess</span>
<p>停止进程。</p>
<b>函数签名:</b>
```go
func StopProcess(pid int) error
```
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/jJZhRYGGcmD)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "10")
if err != nil {
return
}
time.Sleep(1 * time.Second)
err = system.StopProcess(pid)
fmt.Println(err)
// Output:
// <nil>
}
```
### <span id="KillProcess">KillProcess</span>
<p>杀掉进程。</p>
<b>函数签名:</b>
```go
func KillProcess(pid int) error
```
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/XKmvV-ExBWa)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "10")
if err != nil {
return
}
time.Sleep(1 * time.Second)
err = system.KillProcess(pid)
fmt.Println(err)
// Output:
// <nil>
}
```
### <span id="GetProcessInfo">GetProcessInfo</span>
<p>根据进程id获取进程信息。</p>
<b>函数签名:</b>
```go
func GetProcessInfo(pid int) (*ProcessInfo, error)
```
<b>示例:<span style="float:right;display:inline-block">[运行](https://go.dev/play/p/NQDVywEYYx7)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("ls", "-a")
if err != nil {
return
}
processInfo, err := system.GetProcessInfo(pid)
if err != nil {
return
}
fmt.Println(processInfo)
}
```

View File

@@ -35,6 +35,7 @@ import (
- [XError_Info](#XError_Info)
- [XError_Error](#XError_Error)
- [TryUnwrap](#TryUnwrap)
- [TryCatch](#TryCatch)
<div STYLE="page-break-after: always;"></div>
@@ -167,12 +168,12 @@ import (
func main() {
err1 := xerror.New("error").With("level", "high")
err2 := err1.Wrap(errors.New("invalid username"))
err2 := err1.Wrap(errors.New("invalid username"))
fmt.Println(err2.Error())
fmt.Println(err2.Error())
// Output:
// error: invalid username
// Output:
// error: invalid username
}
```
@@ -489,3 +490,56 @@ func main() {
// true
}
```
### <span id="TryCatch">TryCatch</span>
<p>简单实现的java风格异常处理try-catch-finally。try catch不符合go错误处理风格谨慎使用。</p>
<b>函数签名:</b>
```go
func NewTryCatch(ctx context.Context) *TryCatch
func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch
func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch
func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch
func (tc *TryCatch) Do()
```
<b>示例:<span style="float:right;display:inline-block">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/xerror"
)
func main() {
calledFinally := false
calledCatch := false
tc := xerror.NewTryCatch(context.Background())
tc.Try(func(ctx context.Context) error {
return errors.New("error in try block")
}).Catch(func(ctx context.Context, err error) {
calledCatch = true
// Error in try block at /path/xxx.go:{line_number} - Cause: error message
// fmt.Println(err.Error())
}).Finally(func(ctx context.Context) {
calledFinally = true
}).Do()
fmt.Println(calledCatch)
fmt.Println(calledFinally)
// Output:
// true
// true
}
```

View File

@@ -27,7 +27,8 @@ import (
- [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand)
- [TernaryOperator](#TernaryOperator)
- [Ternary](#Ternary)
- [TernaryOperator<sup>deprecated</sup>](#TernaryOperator)
<div STYLE="page-break-after: always;"></div>
@@ -269,9 +270,45 @@ func main() {
### <span id="Ternary">Ternary</span>
<p>Checks the value of param `isTrue`, if true return ifValue else return elseValue</p>
<b>Signature:</b>
```go
func Ternary[T, U any](isTrue T, ifValue U, elseValue U) U
```
<b>Example:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ElllPZY0guT)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/condition"
)
func main() {
conditionTrue := 2 > 1
result1 := condition.Ternary(conditionTrue, 0, 1)
conditionFalse := 2 > 3
result2 := condition.Ternary(conditionFalse, 0, 1)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 0
// 1
}
```
### <span id="TernaryOperator">TernaryOperator</span>
<p>Checks the value of param `isTrue`, if true return ifValue else return elseValue</p>
> ⚠️ This function is deprecated. use `Ternary` instead.
<b>Signature:</b>
```go
@@ -307,4 +344,3 @@ func main() {

View File

@@ -32,6 +32,8 @@ import (
- [AesCfbDecrypt](#AesCfbDecrypt)
- [AesOfbEncrypt](#AesOfbEncrypt)
- [AesOfbDecrypt](#AesOfbDecrypt)
- [AesGcmEncrypt](#AesGcmEncrypt)
- [AesGcmDecrypt](#AesGcmDecrypt)
- [Base64StdEncode](#Base64StdEncode)
- [Base64StdDecode](#Base64StdDecode)
- [DesEcbEncrypt](#DesEcbEncrypt)
@@ -68,6 +70,8 @@ import (
- [GenerateRsaKeyPair](#GenerateRsaKeyPair)
- [RsaEncryptOAEP](#RsaEncryptOAEP)
- [RsaDecryptOAEP](#RsaDecryptOAEP)
- [RsaSign](#RsaSign)
- [RsaVerifySign](#RsaVerifySign)
<div STYLE="page-break-after: always;"></div>
@@ -379,6 +383,74 @@ func main() {
}
```
### <span id="AesGcmEncrypt">AesGcmEncrypt</span>
<p>Encrypt data with key use AES GCM algorithm.</p>
<b>Signature:</b>
```go
func AesGcmEncrypt(data, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/rUt0-DmsPCs)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="AesGcmDecrypt">AesGcmDecrypt</span>
<p>Decrypt data with key use AES GCM algorithm.</p>
<b>Signature:</b>
```go
func AesGcmDecrypt(data, key []byte) []byte
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/rUt0-DmsPCs)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := "hello"
key := "abcdefghijklmnop"
encrypted := cryptor.AesGcmEncrypt([]byte(data), []byte(key))
decrypted := cryptor.AesGcmDecrypt(encrypted, []byte(key))
fmt.Println(string(decrypted))
// Output:
// hello
}
```
### <span id="Base64StdEncode">Base64StdEncode</span>
<p>Encode string with base64 encoding.</p>
@@ -991,13 +1063,13 @@ import (
func main() {
str := "hello"
key := "12345"
key := "12345"
hms := cryptor.HmacSha512WithBase64(str, key)
fmt.Println(hms)
hms := cryptor.HmacSha512WithBase64(str, key)
fmt.Println(hms)
// Output:
// 3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A==
// Output:
// 3Y8SkKndI9NU4lJtmi6c6M///dN8syCADRxsE9Lvw2Mog3ahlsVFja9T+OGqa0Wm2FYwPVwKIGS/+XhYYdSM/A==
}
```
@@ -1054,10 +1126,10 @@ import (
func main() {
md5Str := cryptor.Md5StringWithBase64("hello")
fmt.Println(md5Str)
fmt.Println(md5Str)
// Output:
// XUFAKrxLKna5cZ2REBfFkg==
// Output:
// XUFAKrxLKna5cZ2REBfFkg==
}
```
@@ -1083,10 +1155,10 @@ import (
func main() {
md5Str := cryptor.Md5Byte([]byte{'a'})
fmt.Println(md5Str)
fmt.Println(md5Str)
// Output:
// 0cc175b9c0f1b6a831c399e269772661
// Output:
// 0cc175b9c0f1b6a831c399e269772661
}
```
@@ -1112,10 +1184,10 @@ import (
func main() {
md5Str := cryptor.Md5ByteWithBase64([]byte("hello"))
fmt.Println(md5Str)
fmt.Println(md5Str)
// Output:
// XUFAKrxLKna5cZ2REBfFkg==
// Output:
// XUFAKrxLKna5cZ2REBfFkg==
}
```
@@ -1198,10 +1270,10 @@ import (
func main() {
result := cryptor.Sha1WithBase64("hello")
fmt.Println(result)
fmt.Println(result)
// Output:
// qvTGHdzF6KLavt4PO0gs2a6pQ00=
// Output:
// qvTGHdzF6KLavt4PO0gs2a6pQ00=
}
```
@@ -1258,10 +1330,10 @@ import (
func main() {
result := cryptor.Sha256WithBase64("hello")
fmt.Println(result)
fmt.Println(result)
// Output:
// LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=
// Output:
// LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=
}
```
@@ -1318,10 +1390,10 @@ import (
func main() {
result := cryptor.Sha512WithBase64("hello")
fmt.Println(result)
fmt.Println(result)
// Output:
// m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==
// Output:
// m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==
}
```
@@ -1537,3 +1609,81 @@ func main() {
// hello world
}
```
### <span id="RsaSign">RsaSign</span>
<p>Signs the data with RSA algorithm.</p>
<b>Signature:</b>
```go
func RsaSign(hash crypto.Hash, data []byte, privateKeyFileName string) ([]byte, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := []byte("This is a test data for RSA signing")
hash := crypto.SHA256
privateKey := "./rsa_private.pem"
publicKey := "./rsa_public.pem"
signature, err := RsaSign(hash, data, privateKey)
if err != nil {
return
}
err = RsaVerifySign(hash, data, signature, publicKey)
if err != nil {
return
}
}
```
### <span id="RsaVerifySign">RsaVerifySign</span>
<p>Verifies the signature of the data with RSA algorithm.</p>
<b>Signature:</b>
```go
func RsaVerifySign(hash crypto.Hash, data, signature []byte, pubKeyFileName string) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/cryptor"
)
func main() {
data := []byte("This is a test data for RSA signing")
hash := crypto.SHA256
privateKey := "./rsa_private.pem"
publicKey := "./rsa_public.pem"
signature, err := RsaSign(hash, data, privateKey)
if err != nil {
return
}
err = RsaVerifySign(hash, data, signature, publicKey)
if err != nil {
return
}
}
```

View File

@@ -24,7 +24,7 @@ import (
- [New](#New)
- [FromSlice](#FromSlice)
- [Values](#Values)
- [Values<sup>deprecated</sup>](#Values)
- [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
- [AddIfNotExistBy](#AddIfNotExistBy)
@@ -102,10 +102,11 @@ func main() {
}
```
### <span id="Values">Values<sup>deprecated</sup></span>
### <span id="Values">Values</span>
<p>Return slice of all set data.<br>
The <a href='#ToSlice'>ToSlice()</a> function provides the same functionality as Values and returns a slice containing all values of the set.</p>
<p>Return slice of all set data.</p>
> ⚠️ This function is deprecated. use `ToSlice` instead.
<b>Signature:</b>

View File

@@ -65,6 +65,13 @@ import (
- [TimestampMilli](#TimestampMilli)
- [TimestampMicro](#TimestampMicro)
- [TimestampNano](#TimestampNano)
- [TrackFuncTime](#TrackFuncTime)
- [DaysBetween](#DaysBetween)
- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
- [Min](#Min)
- [Max](#Max)
- [MaxMin](#MaxMin)
<div STYLE="page-break-after: always;"></div>
@@ -1465,3 +1472,198 @@ func main() {
// 1690363051331788000
}
```
### <span id="TrackFuncTime">TrackFuncTime</span>
<p>Tracks function execution time.</p>
<b>Signature:</b>
```go
func TrackFuncTime(pre time.Time) func()
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/QBSEdfXHPTp)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
defer datetime.TrackFuncTime(time.Now())()
var n int
for i := 0; i < 5000000; i++ {
n++
}
fmt.Println(1) // Function main execution time: 1.460287ms
}
```
### <span id="DaysBetween">DaysBetween</span>
<p>Returns the number of days between two times.</p>
<b>Signature:</b>
```go
func DaysBetween(start, end time.Time) int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/qD6qGb3TbOy)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
result := datetime.DaysBetween(start, end)
fmt.Println(result)
// Output:
// 9
}
```
### <span id="GenerateDatetimesBetween">GenerateDatetimesBetween</span>
<p>Returns a slice of strings between two times. `layout`: the format of the datetime string.`interval`: the interval between two datetimes.</p>
<b>Signature:</b>
```go
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/6kHBpAxD9ZC)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
layout := "2006-01-02 15:04:05"
interval := "1h"
result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval)
fmt.Println(result)
fmt.Println(err)
// Output:
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
// <nil>
}
```
### <span id="Min">Min</span>
<p>Returns the earliest time among the given times.</p>
<b>Signature:</b>
```go
func Min(t1 time.Time, times ...time.Time) time.Time
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
minTime := datetime.Min(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC))
fmt.Println(minTime)
// Output:
// 2024-09-01 00:00:00 +0000 UTC
}
```
### <span id="Max">Max</span>
<p>Returns the latest time among the given times.</p>
<b>Signature:</b>
```go
func Max(t1 time.Time, times ...time.Time) time.Time
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
maxTime := datetime.Min(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC))
fmt.Println(maxTime)
// Output:
// 2024-09-02 00:00:00 +0000 UTC
}
```
### <span id="MaxMin">MaxMin</span>
<p>Returns the latest and earliest time among the given times.</p>
<b>Signature:</b>
```go
func MaxMin(t1 time.Time, times ...time.Time) (maxTime time.Time, minTime time.Time)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datetime"
)
func main() {
max, min := datetime.MaxMin(time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 2, 0, 0, 0, 0, time.UTC), time.Date(2024, time.September, 3, 0, 0, 0, 0, time.UTC))
fmt.Println(max)
fmt.Println(min)
// Output:
// 2024-09-03 00:00:00 +0000 UTC
// 2024-09-01 00:00:00 +0000 UTC
}
```

View File

@@ -52,6 +52,7 @@ import (
- [ReadFile](#ReadFile)
- [ChunkRead](#ChunkRead)
- [ParallelChunkRead](#ParallelChunkRead)
- [GetExeOrDllVersion](#GetExeOrDllVersion)
<div STYLE="page-break-after: always;"></div>
@@ -559,7 +560,7 @@ import (
)
func main() {
err := fileutil.Zip("./test.zip", "./unzip/test.txt")
err := fileutil.UnZip("./test.zip", "./test.txt")
if err != nil {
fmt.Println(err)
}
@@ -1074,4 +1075,37 @@ func main() {
// Jim,21,male
// 2
}
```
### <span id="GetExeOrDllVersion">GetExeOrDllVersion</span>
<p>Get the version of exe or dll file on windows.</p>
<b>Signature:</b>
```go
func GetExeOrDllVersion(filePath string) (string, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
v, err := fileutil.GetExeOrDllVersion(`C:\Program Files\Tencent\WeChat\WeChat.exe`)
if err != nil {
panic(err)
}
fmt.Println(v)
// Output:
// 3.9.10.19
}
```

View File

@@ -37,12 +37,12 @@ import (
### <span id="Comma">Comma</span>
<p>Add comma to a number value by every 3 numbers from right to left. ahead by symbol char. if value is a invalid number string like "aa", return empty string.</p>
<p>Add comma to a number value by every 3 numbers from right to left. ahead by a prefix symbol char. if value is a invalid number string like "aa", return empty string.</p>
<b>Signature:</b>
```go
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string
func Comma[T constraints.Float | constraints.Integer | string](value T, prefixSymbol string) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/eRD5k2vzUVX)</span></b>

View File

@@ -28,7 +28,8 @@ import (
- [Before](#Before)
- [CurryFn](#CurryFn)
- [Compose](#Compose)
- [Debounced](#Debounced)
- [Debounce](#Debounce)
- [Debounced<sup>deprecated</sup>](#Debounced)
- [Delay](#Delay)
- [Schedule](#Schedule)
- [Pipeline](#Pipeline)
@@ -40,6 +41,8 @@ import (
- [Xnor](#Xnor)
- [Nand](#Nand)
- [AcceptIf](#AcceptIf)
- [Throttle](#Throttle)
<div STYLE="page-break-after: always;"></div>
@@ -191,11 +194,59 @@ func main() {
// ABCDE
}
```
### <span id="Debounce">Debounce</span>
<p>Creates a debounced version of the provided function. The debounced function will only invoke the original function after the specified delay has passed since the last time it was invoked. It also supports canceling the debounce.</p>
<b>Signature:</b>
```go
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/-dGFrYn_1Zi)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
```
### <span id="Debounced">Debounced</span>
<p>Creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.</p>
> ⚠️ This function is deprecated. use `Debounce` instead.
<b>Signature:</b>
```go
@@ -689,5 +740,46 @@ func main() {
// 0
// false
}
```
### <span id="Throttle">Throttle</span>
<p>Throttle creates a throttled version of the provided function. The returned function guarantees that it will only be invoked at most once per interval.</p>
<b>Signature:</b>
```go
func Throttle(fn func(), interval time.Duration) func()
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/HpoMov-tJSN)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
callCount := 0
fn := func() {
callCount++
}
throttledFn := function.Throttle(fn, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
}
```

View File

@@ -7,6 +7,9 @@ Package maputil includes some functions to manipulate map.
## Source:
- [https://github.com/duke-git/lancet/blob/main/maputil/map.go](https://github.com/duke-git/lancet/blob/main/maputil/map.go)
- [https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go](https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go)
- [https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go](https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go)
<div STYLE="page-break-after: always;"></div>
@@ -47,6 +50,24 @@ import (
- [MapToStruct](#MapToStruct)
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
- [NewOrderedMap](#NewOrderedMap)
- [OrderedMap_Set](#OrderedMap_Set)
- [OrderedMap_Get](#OrderedMap_Get)
- [OrderedMap_Front](#OrderedMap_Front)
- [OrderedMap_Back](#OrderedMap_Back)
- [OrderedMap_Delete](#OrderedMap_Delete)
- [OrderedMap_Clear](#OrderedMap_Clear)
- [OrderedMap_Len](#OrderedMap_Len)
- [OrderedMap_Keys](#OrderedMap_Keys)
- [OrderedMap_Values](#OrderedMap_Values)
- [OrderedMap_Contains](#OrderedMap_Contains)
- [OrderedMap_Range](#OrderedMap_Range)
- [OrderedMap_Elements](#OrderedMap_Elements)
- [OrderedMap_Iter](#OrderedMap_Iter)
- [OrderedMap_ReverseIter](#OrderedMap_ReverseIter)
- [OrderedMap_SortByKey](#OrderedMap_SortByKey)
- [OrderedMap_MarshalJSON](#OrderedMap_MarshalJSON)
- [OrderedMap_UnmarshalJSON](#OrderedMap_UnmarshalJSON)
- [NewConcurrentMap](#NewConcurrentMap)
- [ConcurrentMap_Get](#ConcurrentMap_Get)
- [ConcurrentMap_Set](#ConcurrentMap_Set)
@@ -56,6 +77,8 @@ import (
- [ConcurrentMap_Has](#ConcurrentMap_Has)
- [ConcurrentMap_Range](#ConcurrentMap_Range)
- [GetOrSet](#GetOrSet)
- [SortByKey](#SortByKey)
- [GetOrDefault](#GetOrDefault)
<div STYLE="page-break-after: always;"></div>
@@ -1127,10 +1150,694 @@ func main() {
fmt.Println(values1)
// Output:
// [3 2 1]
// [c b a]
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
// [yesterday today tomorrow]
// [3 2 1]
// [c b a]
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
// [yesterday today tomorrow]
}
```
### <span id="NewOrderedMap">NewOrderedMap</span>
<p>Creates a new OrderedMap.</p>
<b>Signature:</b>
```go
func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V]
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/99QjSYSBdiM)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Set">OrderedMap_Set</span>
<p>Sets the given key-value pair.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Set(key K, value V)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Y4ZJ_oOc1FU)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Get">OrderedMap_Get</span>
<p>Returns the value for the given key.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Get(key K) (V, bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Y4ZJ_oOc1FU)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
```
### <span id="OrderedMap_Delete">OrderedMap_Delete</span>
<p>Deletes the key-value pair for the given key.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Delete(key K)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/5bIi4yaZ3K-)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Delete("b")
fmt.Println(om.Keys())
// Output:
// [a c]
}
```
### <span id="OrderedMap_Clear">OrderedMap_Clear</span>
<p>Clears the map.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Clear()
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/8LwoJyEfuFr)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Clear()
fmt.Println(om.Keys())
// Output:
// []
}
```
### <span id="OrderedMap_Front">OrderedMap_Front</span>
<p>Returns the first key-value pair.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Front() (struct {
Key K
Value V
}, bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ty57XSimpoe)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
frontElement, ok := om.Front()
fmt.Println(frontElement)
fmt.Println(ok)
// Output:
// {a 1}
// true
}
```
### <span id="OrderedMap_Back">OrderedMap_Back</span>
<p>Returns the last key-value pair.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Back() (struct {
Key K
Value V
}, bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/rQMjp1yQmpa)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
backElement, ok := om.Back()
fmt.Println(backElement)
fmt.Println(ok)
// Output:
// {c 3}
// true
}
```
### <span id="OrderedMap_Range">OrderedMap_Range</span>
<p>Calls the given function for each key-value pair.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/U-KpORhc7LZ)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Range(func(key string, value int) bool {
fmt.Println(key, value)
return true
})
// Output:
// a 1
// b 2
// c 3
}
```
### <span id="OrderedMap_Keys">OrderedMap_Keys</span>
<p>Returns the keys in order.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Keys() []K
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Vv_y9ExKclA)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
keys := om.Keys()
fmt.Println(keys)
// Output:
// [a b c]
}
```
### <span id="OrderedMap_Values">OrderedMap_Values</span>
<p>Returns the values in order.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Values() []V
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/TWj5n1-PUfx)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
values := om.Values()
fmt.Println(values)
// Output:
// [1 2 3]
}
```
### <span id="OrderedMap_Elements">OrderedMap_Elements</span>
<p>Returns the key-value pairs in order.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Elements() []struct
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/4BHG4kKz6bB)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
elements := om.Elements()
fmt.Println(elements)
// Output:
// [{a 1} {b 2} {c 3}]
}
```
### <span id="OrderedMap_Len">OrderedMap_Len</span>
<p>Returns the number of key-value pairs.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Len() int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/cLe6z2VX5N-)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Len()
fmt.Println(om.Len())
// Output:
// 3
}
```
### <span id="OrderedMap_Contains">OrderedMap_Contains</span>
<p>Returns true if the given key exists.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Contains(key K) bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/QuwqqnzwDNX)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
result1 := om.Contains("a")
result2 := om.Contains("d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
```
### <span id="OrderedMap_Iter">OrderedMap_Iter</span>
<p>Returns a channel that yields key-value pairs in order.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) Iter() <-chan struct {
Key K
Value V
}
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/tlq2tdvicPt)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.Iter() {
fmt.Println(elem)
}
// Output:
// {a 1}
// {b 2}
// {c 3}
}
```
### <span id="OrderedMap_ReverseIter">OrderedMap_ReverseIter</span>
<p>Returns a channel that yields key-value pairs in reverse order.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) ReverseIter() <-chan struct {
Key K
Value V
}
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/8Q0ssg6hZzO)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.ReverseIter() {
fmt.Println(elem)
}
// Output:
// {c 3}
// {b 2}
// {a 1}
}
```
### <span id="OrderedMap_SortByKey">OrderedMap_SortByKey</span>
<p>Sorts the map by key given less function.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/N7hjD_alZPq)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[int, string]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
om.SortByKey(func(a, b int) bool {
return a < b
})
fmt.Println(om.Elements())
// Output:
// [{1 a} {2 b} {3 c} {4 d}]
}
```
### <span id="OrderedMap_MarshalJSON">OrderedMap_MarshalJSON</span>
<p>Implements the json.Marshaler interface.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/C-wAwydIAC7)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[int, string]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
b, _ := om.MarshalJSON()
fmt.Println(string(b))
// Output:
// {"a":1,"b":2,"c":3}
}
```
### <span id="OrderedMap_UnmarshalJSON">OrderedMap_UnmarshalJSON</span>
<p>Implements the json.Unmarshaler interface.</p>
<b>Signature:</b>
```go
func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/8C3MvJ3-mut)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
om := maputil.NewOrderedMap[string, int]()
data := []byte(`{"a":1,"b":2,"c":3}`)
om.UnmarshalJSON(data)
fmt.Println(om.Elements())
// Output:
// [{a 1} {b 2} {c 3}]
}
```
@@ -1513,7 +2220,7 @@ func main() {
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V
```
<b>Example:<span style="float:right;display:inline-block;"></span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/IVQwO1OkEJC)</span></b>
```go
package main
@@ -1538,4 +2245,83 @@ func main() {
// a
// b
}
```
### <span id="SortByKey">SortByKey</span>
<p>Sorts the map by its keys and returns a new map with sorted keys.</p>
<b>Signature:</b>
```go
func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/PVdmBSnm6P_W)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result := maputil.SortByKey(m, func(a, b int) bool {
return a < b
})
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
```
### <span id="GetOrDefault">GetOrDefault</span>
<p>returns the value of the given key or a default value if the key is not present.</p>
<b>Signature:</b>
```go
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V
```
<b>Example:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/99QjSYSBdiM)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result1 := maputil.GetOrDefault(m, 1, "default")
result2 := maputil.GetOrDefault(m, 6, "default")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// a
// default
}
```

View File

@@ -52,6 +52,10 @@ import (
- [Sum](#Sum)
- [Abs](#Abs)
- [Div](#Div)
- [Variance](#Variance)
- [StdDev](#StdDev)
- [Permutation](#Permutation)
- [Combination](#Combination)
<div STYLE="page-break-after: always;"></div>
@@ -64,7 +68,7 @@ import (
<b>Signature:</b>
```go
func Average[T constraints.Integer | constraints.Float](numbers ...T) T
func Average[T constraints.Integer | constraints.Float](numbers ...T) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Vv7LBwER-pz)</span></b>
@@ -87,7 +91,7 @@ func main() {
fmt.Println(result2)
// Output:
// 1
// 1.5
// 1.3
}
```
@@ -1161,4 +1165,136 @@ func main() {
// 0.5
// 0
}
```
### <span id="Variance">Variance</span>
<p>Returns the variance of numbers.</p>
<b>Signature:</b>
```go
func Variance[T constraints.Float | constraints.Integer](numbers []T) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Variance([]int{1, 2, 3, 4, 5})
result2 := mathutil.Variance([]float64{1.1, 2.2, 3.3, 4.4, 5.5})
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 2
// 2.42
}
```
### <span id="StdDev">StdDev</span>
<p>Returns the standard deviation of numbers.</p>
<b>Signature:</b>
```go
func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.TruncRound(mathutil.StdDev([]int{1, 2, 3, 4, 5}), 2)
result2 := mathutil.TruncRound(mathutil.StdDev([]float64{1.1, 2.2, 3.3, 4.4, 5.5}), 2)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 1.41
// 1.55
}
```
### <span id="Permutation">Permutation</span>
<p>Calculates P(n, k).</p>
<b>Signature:</b>
```go
func Permutation(n, k uint) uint
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Permutation(5, 3)
result2 := mathutil.Permutation(5, 5)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 60
// 120
}
```
### <span id="Combination">Combination</span>
<p>Calculates C(n, k).</p>
<b>Signature:</b>
```go
func Combination(n, k uint) uint
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Combination(5, 3)
result2 := mathutil.Combination(5, 5)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 10
// 1
}
```

View File

@@ -624,7 +624,9 @@ func main() {
### <span id="HttpGet">HttpGet</span>
<p>Send http get request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http get request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -666,7 +668,9 @@ func main() {
### <span id="HttpPost">HttpPost</span>
<p>Send http post request.(Deprecated: use SendRequest for replacement)</p>
<p>Send http post request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -713,7 +717,9 @@ func main() {
### <span id="HttpPut">HttpPut</span>
<p>Send http put request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http put request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -763,7 +769,9 @@ func main() {
### <span id="HttpDelete">HttpDelete</span>
<p>Send http delete request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http delete request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>
@@ -802,7 +810,9 @@ func main() {
### <span id="HttpPatch">HttpPatch</span>
<p>Send http patch request. (Deprecated: use SendRequest for replacement)</p>
<p>Send http patch request.</p>
> ⚠️ This function is deprecated. use `SendRequest` instead.
<b>Signature:</b>

View File

@@ -25,15 +25,22 @@ import (
- [RandBytes](#RandBytes)
- [RandInt](#RandInt)
- [RandString](#RandString)
- [RandFromGivenSlice](#RandFromGivenSlice)
- [RandSliceFromGivenSlice](#RandSliceFromGivenSlice)
- [RandUpper](#RandUpper)
- [RandLower](#RandLower)
- [RandNumeral](#RandNumeral)
- [RandNumeralOrLetter](#RandNumeralOrLetter)
- [RandSymbolChar](#RandSymbolChar)
- [UUIdV4](#UUIdV4)
- [RandIntSlice](#RandIntSlice)
- [RandUniqueIntSlice](#RandUniqueIntSlice)
- [RandFloat](#RandFloat)
- [RandFloats](#RandFloats)
- [RandStringSlice](#RandStringSlice)
- [RandBool](#RandBool)
- [RandBoolSlice](#RandBoolSlice)
- [RandNumberOfLength](#RandNumberOfLength)
<div STYLE="page-break-after: always;"></div>
@@ -117,6 +124,62 @@ func main() {
}
```
### <span id="RandFromGivenSlice">RandFromGivenSlice</span>
<p>Generate a random element from given slice.</p>
<b>Signature:</b>
```go
func RandFromGivenSlice[T any](slice []T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/UrkWueF6yYo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randomSet := []any{"a", 8, "hello", true, 1.1}
randElm := random.RandFromGivenSlice(randomSet)
fmt.Println(randElm)
}
```
### <span id="RandSliceFromGivenSlice">RandSliceFromGivenSlice</span>
<p>Generate a random slice of length num from given slice.</p>
<b>Signature:</b>
```go
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/68UikN9d6VT)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon", "mango", "nectarine", "orange"}
chosen3goods := random.RandSliceFromGivenSlice(goods, 3, false)
fmt.Println(chosen3goods)
}
```
### <span id="RandUpper">RandUpper</span>
<p>Generate a random upper case string</p>
@@ -276,15 +339,41 @@ func main() {
}
```
### <span id="RandIntSlice">RandIntSlice</span>
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
<p>Generate a slice of random int of length n that do not repeat.</p>
<p>Generate a slice of random int. Number range in [min, max)</p>
<b>Signature:</b>
```go
func RandUniqueIntSlice(n, min, max int) []int
func RandIntSlice(length, min, max int) []int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/GATTQ5xTEG8)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
result := random.RandIntSlice(5, 0, 10)
fmt.Println(result) //[1 4 7 1 5] (random)
}
```
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
<p>Generate a slice of random int of length that do not repeat. Number range in [min, max)</p>
<b>Signature:</b>
```go
func RandUniqueIntSlice(length, min, max int) []int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uBkRSOz73Ec)</span></b>
@@ -331,12 +420,12 @@ func main() {
### <span id="RandFloats">RandFloats</span>
<p>Generate a slice of random float64 numbers of length n that do not repeat.</p>
<p>Generate a slice of random float64 numbers of length n that do not repeat. Number range in [min, max)</p>
<b>Signature:</b>
```go
func RandFloats(n int, min, max float64, precision int) []float64
func RandFloats(length int, min, max float64, precision int) []float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
@@ -353,4 +442,112 @@ func main() {
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
fmt.Println(floatNumbers) //[3.42 3.99 1.3 2.38 4.23] (random)
}
```
### <span id="RandStringSlice">RandStringSlice</span>
<p>Generate a slice of random string of length strLen based on charset. chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars. or a combination of them.</p>
<b>Signature:</b>
```go
func RandStringSlice(charset string, sliceLen, strLen int) []string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/2_-PiDv3tGn)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
strs := random.RandStringSlice(random.Letters, 4, 6)
fmt.Println(strs)
// output random string slice like below:
//[CooSMq RUFjDz FAeMPf heRyGv]
}
```
### <span id="RandBool">RandBool</span>
<p>Generate a random boolean value (true or false).</p>
<b>Signature:</b>
```go
func RandBool() bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/to6BLc26wBv)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
result := random.RandBool()
fmt.Println(result) // true or false (random)
}
```
### <span id="RandBoolSlice">RandBoolSlice</span>
<p>Generates a random boolean slice of specified length.</p>
<b>Signature:</b>
```go
func RandBoolSlice(length int) []bool
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/o-VSjPjnILI)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
result := random.RandBoolSlice(2)
fmt.Println(result) // [true false] (random)
}
```
### <span id="RandNumberOfLength">RandNumberOfLength</span>
<p>Generates a random int number of specified length.</p>
<b>Signature:</b>
```go
func RandNumberOfLength(len int) int
```
<b>Signature:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
i := random.RandNumberOfLength(2)
fmt.Println(i) // 21 (random number of length 2)
}
```

View File

@@ -331,7 +331,7 @@ func main() {
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jIm_o2vb5Y4)</span></b>
```go
package main
@@ -361,7 +361,7 @@ func main() {
return errors.New("error occurs")
}
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
err := retry.Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
if err != nil {
return
}
@@ -384,7 +384,7 @@ func main() {
func RetryWithLinearBackoff(interval time.Duration) Option
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/nk2XRmagfVF)</span></b>
```go
package main
@@ -429,7 +429,7 @@ func main() {
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/xp1avQmn16X)</span></b>
```go
package main

View File

@@ -6,7 +6,8 @@ Package slice implements some functions to manipulate slice.
## Source:
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
- [https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go](https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go)
<div STYLE="page-break-after: always;"></div>
@@ -44,6 +45,7 @@ import (
- [EqualWith](#EqualWith)
- [Every](#Every)
- [Filter](#Filter)
- [FilterConcurrent](#FilterConcurrent)
- [Find<sup>deprecated</sup>](#Find)
- [FindBy](#FindBy)
- [FindLast<sup>deprecated</sup>](#FindLast)
@@ -51,6 +53,7 @@ import (
- [Flatten](#Flatten)
- [FlattenDeep](#FlattenDeep)
- [ForEach](#ForEach)
- [ForEachConcurrent](#ForEachConcurrent)
- [ForEachWithBreak](#ForEachWithBreak)
- [GroupBy](#GroupBy)
- [GroupWith](#GroupWith)
@@ -61,11 +64,13 @@ import (
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [Map](#Map)
- [MapConcurrent](#MapConcurrent)
- [FilterMap](#FilterMap)
- [FlatMap](#FlatMap)
- [Merge](#Merge)
- [Reverse](#Reverse)
- [Reduce<sup>deprecated</sup>](#Reduce)
- [ReduceConcurrent](#ReduceConcurrent)
- [ReduceBy](#ReduceBy)
- [ReduceRight](#ReduceRight)
- [Replace](#Replace)
@@ -86,7 +91,9 @@ import (
- [ToSlicePointer](#ToSlicePointer)
- [Unique](#Unique)
- [UniqueBy](#UniqueBy)
- [UniqueByComparator](#UniqueByComparator)
- [UniqueByField](#UniqueByField)
- [UniqueByConcurrent](#UniqueByConcurrent)
- [Union](#Union)
- [UnionBy](#UnionBy)
- [UpdateAt](#UpdateAt)
@@ -98,6 +105,11 @@ import (
- [Break](#Break)
- [RightPadding](#RightPadding)
- [LeftPadding](#LeftPadding)
- [Frequency](#Frequency)
- [JoinFunc](#JoinFunc)
- [ConcatBy](#ConcatBy)
<div STYLE="page-break-after: always;"></div>
@@ -895,10 +907,46 @@ func main() {
}
```
### <span id="Find">Find(deprecated: use FindBy)</span>
### <span id="FilterConcurrent">FilterConcurrent</span>
<p>Applies the provided filter function `predicate` to each element of the input slice concurrently.</p>
<b>Signature:</b>
```go
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/t_pkwerIRVx)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
result := slice.FilterConcurrent(nums, isEven, 2)
fmt.Println(result)
// Output:
// [2 4]
}
```
### <span id="Find">Find</span>
<p>Iterates over elements of slice, returning the first one that passes a truth test on function.</p>
> ⚠️ This function is deprecated. use `FindBy` instead.
<b>Signature:</b>
```go
@@ -967,10 +1015,12 @@ func main() {
}
```
### <span id="FindLast">FindLast(deprecated: use FindLastBy)</span>
### <span id="FindLast">FindLast</span>
<p>iterates over elements of slice from end to begin, returning the last one that passes a truth test on function.</p>
> ⚠️ This function is deprecated. use `FindLastBy` instead.
<b>Signature:</b>
```go
@@ -1134,6 +1184,42 @@ func main() {
}
```
### <span id="ForEachConcurrent">ForEachConcurrent</span>
<p>Applies the iteratee function to each item in the slice concurrently.</p>
<b>Signature:</b>
```go
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/kT4XW7DKVoV)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
result := make([]int, len(nums))
addOne := func(index int, value int) {
result[index] = value + 1
}
slice.ForEachConcurrent(nums, addOne, 4)
fmt.Println(result)
// Output:
// [2 3 4 5 6 7 8 9]
}
```
### <span id="ForEachWithBreak">ForEachWithBreak</span>
<p>Iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop.</p>
@@ -1242,10 +1328,12 @@ func main() {
}
```
### <span id="IntSlice">IntSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
### <span id="IntSlice">IntSlice</span>
<p>Convert interface slice to int slice.</p>
> ⚠️ This function is deprecated. Use generic feature of go1.18+ for replacement.
<b>Signature:</b>
```go
@@ -1271,10 +1359,12 @@ func main() {
}
```
### <span id="InterfaceSlice">InterfaceSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
### <span id="InterfaceSlice">InterfaceSlice</span>
<p>Convert value to interface slice.</p>
> ⚠️ This function is deprecated. Use generic feature of go1.18+ for replacement.
<b>Signature:</b>
```go
@@ -1471,6 +1561,36 @@ func main() {
}
```
### <span id="MapConcurrent">MapConcurrent</span>
<p>Applies the iteratee function to each item in the slice by concrrent.</p>
<b>Signature:</b>
```go
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/H1ehfPkPen0)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6}
result := slice.MapConcurrent(nums, func(_, n int) int { return n * n }, 4)
fmt.Println(result)
// Output:
// [1 4 9 16 25 36]
}
```
### <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>
@@ -1541,10 +1661,12 @@ func main() {
}
```
### <span id="Merge">Merge(deprecated: use Concat)</span>
### <span id="Merge">Merge</span>
<p>Merge all given slices into one slice.</p>
> ⚠️ This function is deprecated. use `Concat` instead.
<b>Signature:</b>
```go
@@ -1604,7 +1726,9 @@ func main() {
### <span id="Reduce">Reduce</span>
<p>Reduce slice.(Deprecated: use ReduceBy)</p>
<p>Reduce slice.</p>
> ⚠️ This function is deprecated. use `ReduceBy` instead.
<b>Signature:</b>
@@ -1636,6 +1760,39 @@ func main() {
}
```
### <span id="ReduceConcurrent">ReduceConcurrent</span>
<p>Reduces the slice to a single value by applying the reducer function to each item in the slice concurrently.</p>
<b>Signature:</b>
```go
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T
```
<b>Example:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Tjwe6OtaG07)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := slice.ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 1)
fmt.Println(result)
// Output:
// 55
}
```
### <span id="ReduceBy">ReduceBy</span>
<p>Produces a value from slice by accumulating the result of each element as passed through the reducer function.</p>
@@ -2130,10 +2287,12 @@ func main() {
}
```
### <span id="StringSlice">StringSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
### <span id="StringSlice">StringSlice</span>
<p>Convert interface slice to string slice.</p>
> ⚠️ This function is deprecated. use generic feature of go1.18+ for replacement
<b>Signature:</b>
```go
@@ -2282,15 +2441,15 @@ func main() {
### <span id="UniqueBy">UniqueBy</span>
<p>Call iteratee func with every item of slice, then remove duplicated.</p>
<p>Removes duplicate elements from the input slice based on the values returned by the iteratee function. this function maintains the order of the elements.</p>
<b>Signature:</b>
```go
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/UR323iZLDpv)</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/GY7JE4yikrl)</span></b>
```go
import (
@@ -2307,7 +2466,73 @@ func main() {
fmt.Println(result)
// Output:
// [1 2 0]
// [1 2 3]
}
```
### <span id="UniqueByComparator">UniqueByComparator</span>
<p>Removes duplicate elements from the input slice using the provided comparator function. The function maintains the order of the elements.</p>
<b>Signature:</b>
```go
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/rwSacr-ZHsR)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
uniqueNums := slice.UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
return item == other
})
caseInsensitiveStrings := slice.UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool {
return strings.ToLower(item) == strings.ToLower(other)
})
fmt.Println(uniqueNums)
fmt.Println(caseInsensitiveStrings)
// Output:
// [1 2 3 4 5 6]
// [apple banana cherry date]
}
```
### <span id="UniqueByConcurrent">UniqueByConcurrent</span>
<p>Removes duplicate elements from the slice by parallel.</p>
<b>Signature:</b>
```go
func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Runs](https://go.dev/play/p/wXZ7LcYRMGL)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/slice"
)
func main() {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool { return item == other }
result := slice.UniqueByConcurrent(nums,comparator, 4)
fmt.Println(result)
// Output:
// [1 2 3 4 5 6 7]
}
```
@@ -2321,7 +2546,7 @@ func main() {
func UniqueByField[T any](slice []T, field string) ([]T, error)
```
<b>Example:<span style="float:right;display:inline-block;"></span></b>
<b>Example:<span style="float:right;display:inline-block;">[Runs](https://go.dev/play/p/6cifcZSPIGu)</span></b>
```go
import (
@@ -2633,14 +2858,14 @@ import (
func main() {
strs := []string{"a", "b", "a", "c", "d", "a"}
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
fmt.Println(modifiedStrs)
fmt.Println(count)
fmt.Println(count)
// Output:
// [ b c d ]
// 3
// [ b c d ]
// 3
}
```
@@ -2677,7 +2902,7 @@ func main() {
}
```
<span id="c">RightPadding</span>
### <span id="RightPadding">RightPadding</span>
<p>RightPadding adds padding to the right end of a slice.</p>
@@ -2696,15 +2921,15 @@ import (
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
nums := []int{1, 2, 3, 4, 5}
padded := slice.RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
}
```
<span id="LeftPadding">LeftPadding</span>
### <span id="LeftPadding">LeftPadding</span>
<p>LeftPadding adds padding to the left begin of a slice.</p>
@@ -2723,10 +2948,116 @@ import (
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
nums := []int{1, 2, 3, 4, 5}
padded := slice.LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
```
### <span id="Frequency">Frequency</span>
<p>Counts the frequency of each element in the slice.</p>
<b>Signature:</b>
```go
func Frequency[T comparable](slice []T) map[T]int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/CW3UVNdUZOq)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
strs := []string{"a", "b", "b", "c", "c", "c"}
result := slice.Frequency(strs)
fmt.Println(result)
// Output:
// map[a:1 b:2 c:3]
}
```
### <span id="JoinFunc">JoinFunc</span>
<p>Joins the slice elements into a single string with the given separator.</p>
<b>Signature:</b>
```go
func JoinFunc[T any](slice []T, sep string, transform func(T) T) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
result := slice.JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string {
return strings.ToUpper(s)
})
fmt.Println(result)
// Output:
// A, B, C
}
```
### <span id="ConcatBy">ConcatBy</span>
<p>Concats the elements of a slice into a single value using the provided separator and connector function.</p>
<b>Signature:</b>
```go
func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
type Person struct {
Name string
Age int
}
people := []Person{
{Name: "Alice", Age: 30},
{Name: "Bob", Age: 25},
{Name: "Charlie", Age: 35},
}
sep := Person{Name: " | ", Age: 0}
personConnector := func(a, b Person) Person {
return Person{Name: a.Name + b.Name, Age: a.Age + b.Age}
}
result := slice.ConcatBy(people, sep, personConnector)
fmt.Println(result.Name)
fmt.Println(result.Age)
// Output:
// Alice | Bob | Charlie
// 90
}
```

View File

@@ -939,3 +939,69 @@ func main() {
// [1 2 3]
}
```
### <span id="IndexOf">IndexOf</span>
<p>Returns the index of the first occurrence of the specified element in this stream, or -1 if this stream does not contain the element.</p>
<b>Signature:</b>
```go
func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/stream"
)
func main() {
s := stream.FromSlice([]int{1, 2, 3, 2})
result1 := s.IndexOf(0, func(a, b int) bool { return a == b })
result2 := s.IndexOf(2, func(a, b int) bool { return a == b })
fmt.Println(result1)
fmt.Println(result2)
// Output:
// -1
// 1
}
```
### <span id="LastIndexOf">LastIndexOf</span>
<p>Returns the index of the last occurrence of the specified element in this stream, or -1 if this stream does not contain the element.</p>
<b>Signature:</b>
```go
func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/stream"
)
func main() {
s := stream.FromSlice([]int{1, 2, 3, 2})
result1 := s.LastIndexOf(0, func(a, b int) bool { return a == b })
result2 := s.LastIndexOf(2, func(a, b int) bool { return a == b })
fmt.Println(result1)
fmt.Println(result2)
// Output:
// -1
// 3
}
```

View File

@@ -63,6 +63,13 @@ import (
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
- [Ellipsis](#Ellipsis)
- [Shuffle](#Shuffle)
- [Rotate](#Rotate)
- [TemplateReplace](#TemplateReplace)
- [RegexMatchAllGroups](#RegexMatchAllGroups)
- [ExtractContent](#RegexMatchAllGroups)
<div STYLE="page-break-after: always;"></div>
@@ -1100,10 +1107,10 @@ import (
func main() {
result1 := strutil.IsNotBlank("")
result2 := strutil.IsNotBlank(" ")
result2 := strutil.IsNotBlank(" ")
result3 := strutil.IsNotBlank("\t\v\f\n")
result4 := strutil.IsNotBlank(" 中文")
result5 := strutil.IsNotBlank(" world ")
result5 := strutil.IsNotBlank(" world ")
fmt.Println(result1)
fmt.Println(result2)
@@ -1544,7 +1551,7 @@ func main() {
func Concat(length int, str ...string) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gD52SZHr4Kp)</span></b>
```go
import (
@@ -1553,17 +1560,205 @@ import (
)
func main() {
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}
```
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
### <span id="Ellipsis">Ellipsis</span>
<p>Truncates a string to a specified length and appends an ellipsis.</p>
<b>Signature:</b>
```go
func Ellipsis(str string, length int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/i1vbdQiQVRR)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Ellipsis("hello world", 5)
result2 := strutil.Ellipsis("你好,世界!", 2)
result3 := strutil.Ellipsis("😀😃😄😁😆", 3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// hello...
// 你好...
// 😀😃😄...
}
```
### <span id="Shuffle">Shuffle</span>
<p>Shuffle the order of characters of given string.</p>
<b>Signature:</b>
```go
func Shuffle(str string) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/iStFwBwyGY7)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result := strutil.Shuffle("hello")
fmt.Println(result) //olelh (random order)
}
```
### <span id="Rotate">Rotate</span>
<p>Rotates the string by the specified number of characters.</p>
<b>Signature:</b>
```go
func Rotate(str string, shift int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Kf03iOeT5bd)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := Rotate("Hello", 0)
result2 := Rotate("Hello", 1)
result3 := Rotate("Hello", 2)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello
// oHell
// loHel
}
```
### <span id="TemplateReplace">TemplateReplace</span>
<p>Replaces the placeholders in the template string with the corresponding values in the data map.The placeholders are enclosed in curly braces, e.g. {key}. for example, the template string is "Hello, {name}!", and the data map is {"name": "world"}, the result will be "Hello, world!".</p>
<b>Signature:</b>
```go
func TemplateReplace(template string, data map[string]string string
```
<b>example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/cXSuFvyZqv9)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
result := strutil.TemplateReplace(template, data)
fmt.Println(result)
// Output:
// Hello, my name is Bob, I'm 20 years old.
}
```
### <span id="RegexMatchAllGroups">RegexMatchAllGroups</span>
<p>Matches all subgroups in a string using a regular expression and returns the result.</p>
<b>Signature:</b>
```go
func RegexMatchAllGroups(pattern, str string) [][]string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/JZiu0RXpgN-)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
str := "Emails: john.doe@example.com and jane.doe@example.com"
result := strutil.RegexMatchAllGroups(pattern, str)
fmt.Println(result[0])
fmt.Println(result[1])
// Output:
// [john.doe@example.com john.doe example com]
// [jane.doe@example.com jane.doe example com]
}
```
### <span id="ExtractContent">ExtractContent</span>
<p>Extracts the content between the start and end strings in the source string.</p>
<b>Signature:</b>
```go
func ExtractContent(s, start, end string) []string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
html := `<span>content1</span>aa<span>content2</span>bb<span>content1</span>`
result := strutil.ExtractContent(html, "<span>", "</span>")
fmt.Println(result)
// Output:
// [content1 content2 content1]
}
```

View File

@@ -31,6 +31,11 @@ import (
- [CompareOsEnv](#CompareOsEnv)
- [ExecCommand](#ExecCommand)
- [GetOsBits](#GetOsBits)
- [StartProcess](#StartProcess)
- [StopProcess](#StopProcess)
- [KillProcess](#KillProcess)
- [GetProcessInfo](#GetProcessInfo)
<div STYLE="page-break-after: always;"></div>
@@ -242,13 +247,14 @@ func main() {
### <span id="ExecCommand">ExecCommand</span>
<p>Execute shell command, return the stdout and stderr string of command, and error if error occur. param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1. In linux, use /bin/bash -c to execute command, In windows, use powershell.exe to execute command.</p>
<p>Execute shell command, return the stdout and stderr string of command, and error if error occur. param `command` is a complete command string, like, ls -a (linux), dir(windows), ping 127.0.0.1. In linux, use /bin/bash -c to execute command, In windows, use powershell.exe to execute command.
The second parameter of the function is the cmd option control parameter. The type is func(*exec.Cmd). You can set the cmd attribute through this parameter.</p>
<b>Signature:</b>
```go
type (
Option func(*exec.Cmd)
Option func(*exec.Cmd)
)
func ExecCommand(command string, opts ...Option) (stdout, stderr string, err error)
```
@@ -263,7 +269,9 @@ import (
func main() {
// linux or mac
stdout, stderr, err := system.ExecCommand("ls")
stdout, stderr, err := system.ExecCommand("ls", func(cmd *exec.Cmd) {
cmd.Dir = "/tmp"
})
fmt.Println("std out: ", stdout)
fmt.Println("std err: ", stderr)
assert.Equal("", stderr)
@@ -285,7 +293,7 @@ func main() {
### <span id="GetOsBits">GetOsBits</span>
<p>Get current os bits, 32bit or 64bit. return 32 or 64</p>
<p>Get current os bits, 32bit or 64bit. return 32 or 64.</p>
<b>Signature:</b>
@@ -306,3 +314,132 @@ func main() {
fmt.Println(osBit) // 32 or 64
}
```
### <span id="StartProcess">StartProcess</span>
<p>Start a new process with the specified name and arguments.</p>
<b>Signature:</b>
```go
func StartProcess(command string, args ...string) (int, error)
```
<b>Example:<span style="float:right;display:inline-block">[Run](https://go.dev/play/p/5GVol6ryS_X)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "2")
if err != nil {
return
}
fmt.Println(pid)
}
```
### <span id="StopProcess">StopProcess</span>
<p>Stop a process by pid.</p>
<b>Signature:</b>
```go
func StopProcess(pid int) error
```
<b>Example:<span style="float:right;display:inline-block">[Run](https://go.dev/play/p/jJZhRYGGcmD)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "10")
if err != nil {
return
}
time.Sleep(1 * time.Second)
err = system.StopProcess(pid)
fmt.Println(err)
// Output:
// <nil>
}
```
### <span id="KillProcess">KillProcess</span>
<p>Kill a process by pid.</p>
<b>Signature:</b>
```go
func KillProcess(pid int) error
```
<b>Example:<span style="float:right;display:inline-block">[Run](https://go.dev/play/p/XKmvV-ExBWa)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("sleep", "10")
if err != nil {
return
}
time.Sleep(1 * time.Second)
err = system.KillProcess(pid)
fmt.Println(err)
// Output:
// <nil>
}
```
### <span id="GetProcessInfo">GetProcessInfo</span>
<p>Retrieves detailed process information by pid.</p>
<b>Signature:</b>
```go
func GetProcessInfo(pid int) (*ProcessInfo, error)
```
<b>Example:<span style="float:right;display:inline-block">[Run](https://go.dev/play/p/NQDVywEYYx7)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/system"
)
func main() {
pid, err := system.StartProcess("ls", "-a")
if err != nil {
return
}
processInfo, err := system.GetProcessInfo(pid)
if err != nil {
return
}
fmt.Println(processInfo)
}
```

View File

@@ -35,6 +35,7 @@ import (
- [XError_Info](#XError_Info)
- [XError_Error](#XError_Error)
- [TryUnwrap](#TryUnwrap)
- [TryCatch](#TryCatch)
<div STYLE="page-break-after: always;"></div>
@@ -166,12 +167,12 @@ import (
func main() {
err1 := xerror.New("error").With("level", "high")
err2 := err1.Wrap(errors.New("invalid username"))
err2 := err1.Wrap(errors.New("invalid username"))
fmt.Println(err2.Error())
fmt.Println(err2.Error())
// Output:
// error: invalid username
// Output:
// error: invalid username
}
```
@@ -487,3 +488,56 @@ func main() {
// true
}
```
### <span id="TryCatch">TryCatch</span>
<p>Simple simulation of Java-style try-catch. It does not align with Go's error-handling philosophy. It is recommended to use it with caution.</p>
<b>Signature:</b>
```go
func NewTryCatch(ctx context.Context) *TryCatch
func (tc *TryCatch) Try(tryFunc func(ctx context.Context) error) *TryCatch
func (tc *TryCatch) Catch(catchFunc func(ctx context.Context, err error)) *TryCatch
func (tc *TryCatch) Finally(finallyFunc func(ctx context.Context)) *TryCatch
func (tc *TryCatch) Do()
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/xerror"
)
func main() {
calledFinally := false
calledCatch := false
tc := xerror.NewTryCatch(context.Background())
tc.Try(func(ctx context.Context) error {
return errors.New("error message ")
}).Catch(func(ctx context.Context, err error) {
calledCatch = true
// Error in try block at /path/xxx.go:{line_number} - Cause: error message
// fmt.Println(err.Error())
}).Finally(func(ctx context.Context) {
calledFinally = true
}).Do()
fmt.Println(calledCatch)
fmt.Println(calledFinally)
// Output:
// true
// true
}
```

View File

@@ -10,7 +10,7 @@ outline: deep
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.4.1. </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.4.4. </b>
```go
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x

View File

@@ -2,7 +2,7 @@
### Sponsor me
<b>Hello, I am a software developer and have been engaged in development work for 13 years. Love open source software. And be willing to put in the energy for it. I am the creator of project [lancet](https://github.com/duke-git/lancet). Since Lancet was released as open source two years ago, it has been used by more than 1,000 internal and external projects. lancet will always be free for all users. Your support is a powerful encouragement for me to continue my struggle. Thanks! You can use WeChat to scans the following QR code or clicks the following sponsor button to initiate sponsorship.</b>
<b>Hello, I am a software developer and have been engaged in development work for 15 years. Love open source software. And be willing to put in the energy for it. I am the creator of project [lancet](https://github.com/duke-git/lancet). Since Lancet was released as open source in 2021, it has been used by more than 1700 internal and external projects. lancet will always be free for all users. Your support is a powerful encouragement for me to continue my struggle. Thanks! You can use WeChat to scans the following QR code or clicks the following sponsor button to initiate sponsorship.</b>
<style>
.sponsor-ctn {

View File

@@ -10,7 +10,7 @@ outline: deep
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
```
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.1。</b>
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.4。</b>
```go
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x

View File

@@ -1,6 +1,6 @@
### 赞助
<b>您好,我是一名软件开发者,从事开发工作 13 年。热爱软件开源。并愿意为此付出精力。是开源项目[lancet](https://github.com/duke-git/lancet)的作者。Lancet 自两年前开源发布以来,已有超过 1000 个内外部项目使用。lancet 一直会对所有用户免费。您的支持是对我继续奋斗的有力鼓励。谢谢! 微信扫描以下二维码或点击以下赞助按钮发起赞助。 </b>
<b>您好,我是一名软件开发者,从事开发工作15年。热爱软件开源。并愿意为此付出精力。是开源项目[lancet](https://github.com/duke-git/lancet)的作者。Lancet自2021年前开源发布以来,已有超过1700个内外部项目使用。lancet一直会对所有用户免费。您的支持是对我继续奋斗的有力鼓励。谢谢! 微信扫描以下二维码或点击以下赞助按钮发起赞助。 </b>
<style>
.sponsor-ctn {

View File

@@ -14,6 +14,7 @@ import (
"encoding/csv"
"errors"
"fmt"
"github.com/duke-git/lancet/v2/validator"
"io"
"io/fs"
"net/http"
@@ -23,8 +24,6 @@ import (
"sort"
"strings"
"sync"
"github.com/duke-git/lancet/v2/validator"
)
// FileReader is a reader supporting offset seeking and reading one

View File

@@ -192,6 +192,27 @@ func ExampleListFileNames() {
// [assert.go assert_test.go error_join.go]
}
func ExampleMiMeType() {
fname := "./test.txt"
file, _ := os.Create(fname)
_, err := file.WriteString("hello\nworld")
if err != nil {
return
}
f, _ := os.Open(fname)
defer f.Close()
mimeType := MiMeType(f)
fmt.Println(mimeType)
os.Remove(fname)
// Output:
// application/octet-stream
}
func ExampleZip() {
srcFile := "./test.txt"
CreateFile(srcFile)
@@ -214,24 +235,20 @@ func ExampleZip() {
}
func ExampleUnZip() {
fname := "./test.txt"
file, _ := os.Create(fname)
zipFile := "./testdata/file.go.zip"
_, err := file.WriteString("hello\nworld")
err := UnZip(zipFile, "./testdata")
if err != nil {
return
}
f, _ := os.Open(fname)
defer f.Close()
exist := IsExist("./testdata/file.go")
fmt.Println(exist)
mimeType := MiMeType(f)
fmt.Println(mimeType)
os.Remove(fname)
os.Remove("./testdata/file.go")
// Output:
// application/octet-stream
// true
}
func ExampleZipAppendEntry() {

83
fileutil/file_windows.go Normal file
View File

@@ -0,0 +1,83 @@
//go:build windows
package fileutil
import (
"fmt"
"syscall"
"unsafe"
)
// tagVS_FIXEDFILEINFO 参考结构体https://learn.microsoft.com/zh-cn/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
type tagVS_FIXEDFILEINFO struct {
Signature uint32
StructVersion uint32
FileVersionMS uint32
FileVersionLS uint32
ProductVersionMS uint32
ProductVersionLS uint32
FileFlagsMask uint32
FileFlags uint32
FileOS uint32
FileType uint32
FileSubtype uint32
FileDateMS uint32
FileDateLS uint32
}
// GetExeOrDllVersion get the version of exe or dll file on windows.
// Play: todo
func GetExeOrDllVersion(filePath string) (string, error) {
// 加载系统dll
versionDLL := syscall.NewLazyDLL("version.dll")
getFileVersionInfoSize := versionDLL.NewProc("GetFileVersionInfoSizeW")
getFileVersionInfo := versionDLL.NewProc("GetFileVersionInfoW")
verQueryValue := versionDLL.NewProc("VerQueryValueW")
// 转换路径为UTF-16
filePathPtr, err := syscall.UTF16PtrFromString(filePath)
if err != nil {
return "", fmt.Errorf("unable to convert file path to UTF-16: %w", err)
}
// 获取version信息大小
size, _, err := getFileVersionInfoSize.Call(
uintptr(unsafe.Pointer(filePathPtr)),
0,
)
if size == 0 {
return "", fmt.Errorf("unable to obtain version information size: %v", err)
}
// 加载version信息
data := make([]byte, size)
ret, _, err := getFileVersionInfo.Call(uintptr(unsafe.Pointer(filePathPtr)), 0, size, uintptr(unsafe.Pointer(&data[0])))
if ret == 0 {
return "", fmt.Errorf("unable to obtain version information: %v", err)
}
// 查询version信息
var fixedInfo *tagVS_FIXEDFILEINFO
var fixedInfoLen uint32
u16, err := syscall.UTF16PtrFromString(`\`)
if err != nil {
return "", fmt.Errorf("unable to convert file path to UTF-16: %w", err)
}
ret, _, err = verQueryValue.Call(
uintptr(unsafe.Pointer(&data[0])),
uintptr(unsafe.Pointer(u16)),
uintptr(unsafe.Pointer(&fixedInfo)),
uintptr(unsafe.Pointer(&fixedInfoLen)),
)
if ret == 0 {
return "", fmt.Errorf("unable to query version information: %v", err)
}
// 转换结构体
major := fixedInfo.FileVersionMS >> 16
minor := fixedInfo.FileVersionMS & 0xFFFF
build := fixedInfo.FileVersionLS >> 16
revision := fixedInfo.FileVersionLS & 0xFFFF
return fmt.Sprintf("%d.%d.%d.%d", major, minor, build, revision), nil
}

View File

@@ -0,0 +1,13 @@
//go:build windows
package fileutil
import "testing"
func TestGetExeOrDllVersion(t *testing.T) {
v, err := GetExeOrDllVersion(`C:\Windows\System32\cmd.exe`)
if err != nil {
t.Error(err)
}
t.Log(v)
}

View File

@@ -6,6 +6,9 @@ import (
"strconv"
"strings"
"unicode"
"github.com/duke-git/lancet/v2/mathutil"
"github.com/duke-git/lancet/v2/strutil"
)
//
@@ -15,63 +18,63 @@ import (
// http://en.wikipedia.org/wiki/Binary_prefix
const (
// Decimal
UnitB = 1
UnitKB = 1000
UnitMB = 1000 * UnitKB
UnitGB = 1000 * UnitMB
UnitTB = 1000 * UnitGB
UnitPB = 1000 * UnitTB
UnitEB = 1000 * UnitPB
unitB = 1
unitKB = 1000
unitMB = 1000 * unitKB
unitGB = 1000 * unitMB
unitTB = 1000 * unitGB
unitPB = 1000 * unitTB
unitEB = 1000 * unitPB
// Binary
UnitBiB = 1
UnitKiB = 1024
UnitMiB = 1024 * UnitKiB
UnitGiB = 1024 * UnitMiB
UnitTiB = 1024 * UnitGiB
UnitPiB = 1024 * UnitTiB
UnitEiB = 1024 * UnitPiB
unitBiB = 1
unitKiB = 1024
unitMiB = 1024 * unitKiB
unitGiB = 1024 * unitMiB
unitTiB = 1024 * unitGiB
unitPiB = 1024 * unitTiB
unitEiB = 1024 * unitPiB
)
// type byteUnitMap map[byte]int64
var (
decimalByteMap = map[string]uint64{
"b": UnitB,
"kb": UnitKB,
"mb": UnitMB,
"gb": UnitGB,
"tb": UnitTB,
"pb": UnitPB,
"eb": UnitEB,
"b": unitB,
"kb": unitKB,
"mb": unitMB,
"gb": unitGB,
"tb": unitTB,
"pb": unitPB,
"eb": unitEB,
// Without suffix
"": UnitB,
"k": UnitKB,
"m": UnitMB,
"g": UnitGB,
"t": UnitTB,
"p": UnitPB,
"e": UnitEB,
"": unitB,
"k": unitKB,
"m": unitMB,
"g": unitGB,
"t": unitTB,
"p": unitPB,
"e": unitEB,
}
binaryByteMap = map[string]uint64{
"bi": UnitBiB,
"kib": UnitKiB,
"mib": UnitMiB,
"gib": UnitGiB,
"tib": UnitTiB,
"pib": UnitPiB,
"eib": UnitEiB,
"bi": unitBiB,
"kib": unitKiB,
"mib": unitMiB,
"gib": unitGiB,
"tib": unitTiB,
"pib": unitPiB,
"eib": unitEiB,
// Without suffix
"": UnitBiB,
"ki": UnitKiB,
"mi": UnitMiB,
"gi": UnitGiB,
"ti": UnitTiB,
"pi": UnitPiB,
"ei": UnitEiB,
"": unitBiB,
"ki": unitKiB,
"mi": unitMiB,
"gi": unitGiB,
"ti": unitTiB,
"pi": unitPiB,
"ei": unitEiB,
}
)
@@ -84,26 +87,28 @@ var (
// The precision parameter specifies the number of digits after the decimal point, which defaults to 4.
// Play: https://go.dev/play/p/FPXs1suwRcs
func DecimalBytes(size float64, precision ...int) string {
p := 5
pointPosition := 4
if len(precision) > 0 {
p = precision[0] + 1
pointPosition = precision[0]
}
size, unit := calculateByteSize(size, 1000.0, decimalByteUnits)
return fmt.Sprintf("%.*g%s", p, size, unit)
return roundToToString(size, pointPosition) + unit
}
// BinaryBytes returns a human-readable byte size under binary standard (base 1024)
// The precision parameter specifies the number of digits after the decimal point, which defaults to 4.
// Play: https://go.dev/play/p/G9oHHMCAZxP
func BinaryBytes(size float64, precision ...int) string {
p := 5
pointPosition := 4
if len(precision) > 0 {
p = precision[0] + 1
pointPosition = precision[0]
}
size, unit := calculateByteSize(size, 1024.0, binaryByteUnits)
return fmt.Sprintf("%.*g%s", p, size, unit)
return roundToToString(size, pointPosition) + unit
}
func calculateByteSize(size float64, base float64, byteUnits []string) (float64, string) {
@@ -116,6 +121,29 @@ func calculateByteSize(size float64, base float64, byteUnits []string) (float64,
return size, byteUnits[i]
}
func roundToToString(x float64, max ...int) string {
pointPosition := 4
if len(max) > 0 {
pointPosition = max[0]
}
result := mathutil.RoundToString(x, pointPosition)
// 删除小数位结尾的0
decimal := strings.TrimRight(strutil.After(result, "."), "0")
if decimal == "" || pointPosition == 0 {
// 没有小数位直接返回整数
return strutil.Before(result, ".")
}
// 小数位大于想要设置的位数,按需要截断
if len(decimal) > pointPosition {
return strutil.Before(result, ".") + "." + decimal[:pointPosition]
}
// 小数位小于等于想要的位数,直接拼接返回
return strutil.Before(result, ".") + "." + decimal
}
// ParseDecimalBytes return the human readable bytes size string into the amount it represents(base 1000).
// ParseDecimalBytes("42 MB") -> 42000000, nil
// Play: https://go.dev/play/p/Am98ybWjvjj

View File

@@ -15,11 +15,19 @@ func TestDecimalBytes(t *testing.T) {
assert.Equal("1.024KB", DecimalBytes(1024))
assert.Equal("1.2346MB", DecimalBytes(1234567))
assert.Equal("1.235MB", DecimalBytes(1234567, 3))
assert.Equal("1.123GB", DecimalBytes(float64(1.123*UnitGB)))
assert.Equal("2.123TB", DecimalBytes(float64(2.123*UnitTB)))
assert.Equal("3.123PB", DecimalBytes(float64(3.123*UnitPB)))
assert.Equal("4.123EB", DecimalBytes(float64(4.123*UnitEB)))
assert.Equal("1EB", DecimalBytes(float64(1000*UnitPB)))
assert.Equal("1.123GB", DecimalBytes(float64(1.123*unitGB)))
assert.Equal("2.123TB", DecimalBytes(float64(2.123*unitTB)))
assert.Equal("3.123PB", DecimalBytes(float64(3.123*unitPB)))
assert.Equal("4.123EB", DecimalBytes(float64(4.123*unitEB)))
assert.Equal("1EB", DecimalBytes(float64(1000*unitPB)))
assert.Equal("62MB", DecimalBytes(61812496, 0))
assert.Equal("61.8MB", DecimalBytes(61812496, 1))
assert.Equal("401MB", DecimalBytes(401000000))
assert.Equal("401MB", DecimalBytes(401000000, 0))
assert.Equal("4MB", DecimalBytes(4010000, 0))
assert.Equal("4MB", DecimalBytes(4000000, 0))
assert.Equal("4MB", DecimalBytes(4000000, 1))
}
func TestBinaryBytes(t *testing.T) {
@@ -31,6 +39,10 @@ func TestBinaryBytes(t *testing.T) {
assert.Equal("1MiB", BinaryBytes(1024*1024))
assert.Equal("1.1774MiB", BinaryBytes(1234567))
assert.Equal("1.18MiB", BinaryBytes(1234567, 2))
assert.Equal("10KiB", BinaryBytes(10240, 0))
assert.Equal("10KiB", BinaryBytes(10240, 1))
assert.Equal("10KiB", BinaryBytes(10240, 2))
}
func TestParseDecimalBytes(t *testing.T) {

View File

@@ -14,11 +14,11 @@ import (
"golang.org/x/exp/constraints"
)
// Comma add comma to a number value by every 3 numbers from right. ahead by symbol char.
// Comma add comma to a number value by every 3 numbers from right. ahead by prefix symbol char.
// if value is invalid number string eg "aa", return empty string
// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345"
// Play: https://go.dev/play/p/eRD5k2vzUVX
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string {
func Comma[T constraints.Float | constraints.Integer | string](value T, prefixSymbol string) string {
numString := convertor.ToString(value)
_, err := strconv.ParseFloat(numString, 64)
@@ -26,17 +26,26 @@ func Comma[T constraints.Float | constraints.Integer | string](value T, symbol s
return ""
}
isNegative := strings.HasPrefix(numString, "-")
if isNegative {
numString = numString[1:]
}
index := strings.Index(numString, ".")
if index == -1 {
index = len(numString)
}
for index > 3 {
index = index - 3
index -= 3
numString = numString[:index] + "," + numString[index:]
}
return symbol + numString
if isNegative {
numString = "-" + numString
}
return prefixSymbol + numString
}
// Pretty data to JSON string.

View File

@@ -28,6 +28,10 @@ func TestComma(t *testing.T) {
assert.Equal("12,345.6789", Comma(+12345.6789, ""))
assert.Equal("12,345,678.9", Comma(12345678.9, ""))
assert.Equal("123,456,789,000", Comma(123456789000, ""))
assert.Equal("-999", Comma(-999, ""))
assert.Equal("-1,000", Comma(-1000, ""))
assert.Equal("-1,234,567", Comma(-1234567, ""))
}
func TestPretty(t *testing.T) {

View File

@@ -7,6 +7,7 @@ package function
import (
"fmt"
"reflect"
"sync"
"time"
)
@@ -84,36 +85,97 @@ func Delay(delay time.Duration, fn any, args ...any) {
}
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
// Deprecated: Use Debounce function instead.
// Play: https://go.dev/play/p/absuEGB_GN7
func Debounced(fn func(), duration time.Duration) func() {
// Catch programming error while constructing the closure
mustBeFunction(fn)
func Debounced(fn func(), delay time.Duration) func() {
debouncedFn, _ := Debounce(fn, delay)
return debouncedFn
}
timer := time.NewTimer(duration)
timer.Stop()
// Debounce creates a debounced version of the provided function.
// Play: https://go.dev/play/p/-dGFrYn_1Zi
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func()) {
var (
timer *time.Timer
mu sync.Mutex
)
go func() {
for {
<-timer.C
go fn()
debouncedFn = func() {
mu.Lock()
defer mu.Unlock()
if timer != nil {
timer.Stop()
}
}()
return func() { timer.Reset(duration) }
timer = time.AfterFunc(delay, func() {
fn()
})
}
cancelFn = func() {
mu.Lock()
defer mu.Unlock()
if timer != nil {
timer.Stop()
}
}
return debouncedFn, cancelFn
}
// Throttle creates a throttled version of the provided function.
// The returned function guarantees that it will only be invoked at most once per interval.
// Play: https://go.dev/play/p/HpoMov-tJSN
func Throttle(fn func(), interval time.Duration) func() {
var (
timer *time.Timer
lastRun time.Time
mu sync.Mutex
)
return func() {
mu.Lock()
defer mu.Unlock()
now := time.Now()
if now.Sub(lastRun) >= interval {
fn()
lastRun = now
if timer != nil {
timer.Stop()
timer = nil
}
} else if timer == nil {
delay := interval - now.Sub(lastRun)
timer = time.AfterFunc(delay, func() {
mu.Lock()
defer mu.Unlock()
fn()
lastRun = time.Now()
timer = nil
})
}
}
}
// Schedule invoke function every duration time, util close the returned bool channel.
// Play: https://go.dev/play/p/hbON-Xeyn5N
func Schedule(d time.Duration, fn any, args ...any) chan bool {
func Schedule(duration time.Duration, fn any, args ...any) chan bool {
// Catch programming error while constructing the closure
mustBeFunction(fn)
quit := make(chan bool)
go func() {
for {
unsafeInvokeFunc(fn, args...)
select {
case <-time.After(d):
case <-time.After(duration):
case <-quit:
return
}

View File

@@ -79,6 +79,32 @@ func ExampleDelay() {
// hello
}
func ExampleDebounce() {
callCount := 0
fn := func() {
callCount++
}
debouncedFn, _ := Debounce(fn, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
debouncedFn()
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
// 2
}
func ExampleDebounced() {
count := 0
add := func() {
@@ -174,3 +200,24 @@ func ExampleAcceptIf() {
// 0
// false
}
func ExampleThrottle() {
callCount := 0
fn := func() {
callCount++
}
throttledFn := Throttle(fn, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
fmt.Println(callCount)
// Output:
// 1
}

View File

@@ -125,24 +125,178 @@ func TestDebounced(t *testing.T) {
assert.Equal(2, count)
}
func TestDebounce(t *testing.T) {
assert := internal.NewAssert(t, "TestDebounce")
t.Run("Single call", func(t *testing.T) {
callCount := 0
debouncedFn, _ := Debounce(func() {
callCount++
}, 500*time.Millisecond)
debouncedFn()
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Multiple calls within debounce interval", func(t *testing.T) {
callCount := 0
debouncedFn, _ := Debounce(func() {
callCount++
}, 1*time.Second)
for i := 0; i < 5; i++ {
go func(index int) {
time.Sleep(time.Duration(index) * 200 * time.Millisecond)
debouncedFn()
}(i)
}
time.Sleep(2 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Immediate consecutive calls", func(t *testing.T) {
callCount := 0
debouncedFn, _ := Debounce(func() {
callCount++
}, 500*time.Millisecond)
for i := 0; i < 10; i++ {
debouncedFn()
time.Sleep(50 * time.Millisecond)
}
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Cancel calls", func(t *testing.T) {
callCount := 0
debouncedFn, cancelFn := Debounce(func() {
callCount++
}, 500*time.Millisecond)
debouncedFn()
cancelFn()
time.Sleep(1 * time.Second)
assert.Equal(0, callCount)
})
}
func TestThrottle(t *testing.T) {
assert := internal.NewAssert(t, "TestThrottle")
t.Run("Single call", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 1*time.Second)
throttledFn()
time.Sleep(100 * time.Millisecond)
assert.Equal(1, callCount)
})
t.Run("Multiple calls within throttle interval", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 1*time.Second)
for i := 0; i < 5; i++ {
throttledFn()
}
time.Sleep(1 * time.Second)
assert.Equal(1, callCount)
})
t.Run("Mutiple calls space out throttle interval", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 500*time.Millisecond)
throttledFn()
time.Sleep(600 * time.Millisecond)
throttledFn()
time.Sleep(600 * time.Millisecond)
throttledFn()
time.Sleep(1 * time.Second)
assert.Equal(3, callCount)
})
t.Run("Call function near the end of the interval", func(t *testing.T) {
callCount := 0
throttledFn := Throttle(func() {
callCount++
}, 1*time.Second)
throttledFn()
time.Sleep(900 * time.Millisecond)
throttledFn()
time.Sleep(200 * time.Millisecond)
assert.Equal(2, callCount)
})
}
func TestSchedule(t *testing.T) {
// assert := internal.NewAssert(t, "TestSchedule")
assert := internal.NewAssert(t, "TestSchedule")
var res []string
appendStr := func(s string) {
res = append(res, s)
}
t.Run("Single call", func(t *testing.T) {
res := []string{}
appendStr := func(s string) {
res = append(res, s)
}
stop := Schedule(200*time.Millisecond, appendStr, "*")
close(stop)
stop := Schedule(1*time.Second, appendStr, "*")
time.Sleep(5 * time.Second)
close(stop)
time.Sleep(400 * time.Millisecond)
t.Log(res)
assert.Equal([]string{"*"}, res)
})
t.Run("Multiple calls", func(t *testing.T) {
res := []string{}
appendStr := func(s string) {
res = append(res, s)
}
stop := Schedule(200*time.Millisecond, appendStr, "*")
time.Sleep(800 * time.Millisecond)
close(stop)
assert.Equal([]string{"*", "*", "*", "*"}, res)
})
// todo: in github action, for now, this test is not working sometimes
// res maybe [* * * * *] or [* * * * * *]
// expected := []string{"*", "*", "*", "*", "*"}
// assert.Equal(expected, res)
}
func TestPipeline(t *testing.T) {

2
go.sum
View File

@@ -1,6 +1,4 @@
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=

View File

@@ -70,7 +70,12 @@ func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T
// 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 {
result := make(map[K]V, 0)
size := 0
for i := range maps {
size += len(maps[i])
}
result := make(map[K]V, size)
for _, m := range maps {
for k, v := range m {
@@ -438,7 +443,7 @@ func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator fun
}
// GetOrSet returns value of the given key or set the given value value if not present.
// Play: todo
// Play: https://go.dev/play/p/IVQwO1OkEJC
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V {
if v, ok := m[key]; ok {
return v
@@ -448,3 +453,216 @@ func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V {
return value
}
// SortByKey sorts the map by its keys and returns a new map with sorted keys.
// Play: https://go.dev/play/p/PVdmBSnm6P_W
func SortByKey[K constraints.Ordered, V any](m map[K]V, less func(a, b K) bool) (sortedKeysMap map[K]V) {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return less(keys[i], keys[j])
})
sortedKeysMap = make(map[K]V, len(m))
for _, k := range keys {
sortedKeysMap[k] = m[k]
}
return
}
var mapHandlers = map[reflect.Kind]func(reflect.Value, reflect.Value) error{
reflect.String: convertNormal,
reflect.Int: convertNormal,
reflect.Int16: convertNormal,
reflect.Int32: convertNormal,
reflect.Int64: convertNormal,
reflect.Uint: convertNormal,
reflect.Uint16: convertNormal,
reflect.Uint32: convertNormal,
reflect.Uint64: convertNormal,
reflect.Float32: convertNormal,
reflect.Float64: convertNormal,
reflect.Uint8: convertNormal,
reflect.Int8: convertNormal,
reflect.Struct: convertNormal,
reflect.Complex64: convertNormal,
reflect.Complex128: convertNormal,
}
var _ = func() struct{} {
mapHandlers[reflect.Map] = convertMap
mapHandlers[reflect.Array] = convertSlice
mapHandlers[reflect.Slice] = convertSlice
return struct{}{}
}()
// MapTo try to map any interface to struct or base type
/*
Eg:
v := map[string]interface{}{
"service":map[string]interface{}{
"ip":"127.0.0.1",
"port":1234,
},
version:"v1.0.01"
}
type Target struct {
Service struct {
Ip string `json:"ip"`
Port int `json:"port"`
} `json:"service"`
Ver string `json:"version"`
}
var dist Target
if err := maputil.MapTo(v,&dist); err != nil {
log.Println(err)
return
}
log.Println(dist)
*/
// Play: https://go.dev/play/p/4K7KBEPgS5M
func MapTo(src any, dst any) error {
dstRef := reflect.ValueOf(dst)
if dstRef.Kind() != reflect.Ptr {
return fmt.Errorf("dst is not ptr")
}
dstElem := dstRef.Type().Elem()
if dstElem.Kind() == reflect.Struct {
srcMap := src.(map[string]interface{})
return MapToStruct(srcMap, dst)
}
dstRef = reflect.Indirect(dstRef)
srcRef := reflect.ValueOf(src)
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
srcRef = srcRef.Elem()
}
if f, ok := mapHandlers[srcRef.Kind()]; ok {
return f(srcRef, dstRef)
}
return fmt.Errorf("no implemented:%s", srcRef.Type())
}
func convertNormal(src reflect.Value, dst reflect.Value) error {
if dst.CanSet() {
if src.Type() == dst.Type() {
dst.Set(src)
} else if src.CanConvert(dst.Type()) {
dst.Set(src.Convert(dst.Type()))
} else {
return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String())
}
}
return nil
}
func convertSlice(src reflect.Value, dst reflect.Value) error {
if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice {
return fmt.Errorf("error type:%s", dst.Type().String())
}
l := src.Len()
target := reflect.MakeSlice(dst.Type(), l, l)
if dst.CanSet() {
dst.Set(target)
}
for i := 0; i < l; i++ {
srcValue := src.Index(i)
if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface {
srcValue = srcValue.Elem()
}
if f, ok := mapHandlers[srcValue.Kind()]; ok {
err := f(srcValue, dst.Index(i))
if err != nil {
return err
}
}
}
return nil
}
func convertMap(src reflect.Value, dst reflect.Value) error {
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
if src.Kind() == reflect.Interface && dst.IsValid() {
return convertMap(src.Elem(), dst)
} else {
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
}
}
dstType := dst.Type()
num := dstType.NumField()
exist := map[string]int{}
for i := 0; i < num; i++ {
k := dstType.Field(i).Tag.Get("json")
if k == "" {
k = dstType.Field(i).Name
}
if strings.Contains(k, ",") {
taglist := strings.Split(k, ",")
if taglist[0] == "" {
k = dstType.Field(i).Name
} else {
k = taglist[0]
}
}
exist[k] = i
}
keys := src.MapKeys()
for _, key := range keys {
if index, ok := exist[key.String()]; ok {
v := dst.Field(index)
if v.Kind() == reflect.Struct {
err := convertMap(src.MapIndex(key), v)
if err != nil {
return err
}
} else {
if v.CanSet() {
if v.Type() == src.MapIndex(key).Elem().Type() {
v.Set(src.MapIndex(key).Elem())
} else if src.MapIndex(key).Elem().CanConvert(v.Type()) {
v.Set(src.MapIndex(key).Elem().Convert(v.Type()))
} else if f, ok := mapHandlers[src.MapIndex(key).Elem().Kind()]; ok && f != nil {
err := f(src.MapIndex(key).Elem(), v)
if err != nil {
return err
}
} else {
return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type())
}
}
}
}
}
return nil
}
// GetOrDefault returns the value of the given key or a default value if the key is not present.
// Play: https://go.dev/play/p/99QjSYSBdiM
func GetOrDefault[K comparable, V any](m map[K]V, key K, defaultValue V) V {
if v, ok := m[key]; ok {
return v
}
return defaultValue
}

View File

@@ -540,3 +540,292 @@ func ExampleGetOrSet() {
// a
// b
}
func ExampleSortByKey() {
m := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result := SortByKey(m, func(a, b int) bool {
return a < b
})
fmt.Println(result)
// Output:
// map[1:a 2:b 3:c 4:d]
}
func ExampleOrderedMap_Set() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
func ExampleOrderedMap_Get() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
val1, ok := om.Get("a")
fmt.Println(val1, ok)
val2, ok := om.Get("d")
fmt.Println(val2, ok)
// Output:
// 1 true
// 0 false
}
func ExampleOrderedMap_Front() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
frontElement, ok := om.Front()
fmt.Println(frontElement)
fmt.Println(ok)
// Output:
// {a 1}
// true
}
func ExampleOrderedMap_Back() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
backElement, ok := om.Back()
fmt.Println(backElement)
fmt.Println(ok)
// Output:
// {c 3}
// true
}
func ExampleOrderedMap_Delete() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Delete("b")
fmt.Println(om.Keys())
// Output:
// [a c]
}
func ExampleOrderedMap_Clear() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Clear()
fmt.Println(om.Keys())
// Output:
// []
}
func ExampleOrderedMap_Len() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Len()
fmt.Println(om.Len())
// Output:
// 3
}
func ExampleOrderedMap_Keys() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
keys := om.Keys()
fmt.Println(keys)
// Output:
// [a b c]
}
func ExampleOrderedMap_Values() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
values := om.Values()
fmt.Println(values)
// Output:
// [1 2 3]
}
func ExampleOrderedMap_Contains() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
result1 := om.Contains("a")
result2 := om.Contains("d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
func ExampleOrderedMap_Range() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Range(func(key string, value int) bool {
fmt.Println(key, value)
return true
})
// Output:
// a 1
// b 2
// c 3
}
func ExampleOrderedMap_Elements() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
elements := om.Elements()
fmt.Println(elements)
// Output:
// [{a 1} {b 2} {c 3}]
}
func ExampleOrderedMap_Iter() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.Iter() {
fmt.Println(elem)
}
// Output:
// {a 1}
// {b 2}
// {c 3}
}
func ExampleOrderedMap_ReverseIter() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
for elem := range om.ReverseIter() {
fmt.Println(elem)
}
// Output:
// {c 3}
// {b 2}
// {a 1}
}
func ExampleOrderedMap_SortByKey() {
om := NewOrderedMap[int, string]()
om.Set(3, "c")
om.Set(1, "a")
om.Set(4, "d")
om.Set(2, "b")
om.SortByKey(func(a, b int) bool {
return a < b
})
fmt.Println(om.Elements())
// Output:
// [{1 a} {2 b} {3 c} {4 d}]
}
func ExampleOrderedMap_MarshalJSON() {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
b, _ := om.MarshalJSON()
fmt.Println(string(b))
// Output:
// {"a":1,"b":2,"c":3}
}
func ExampleOrderedMap_UnmarshalJSON() {
// om := NewOrderedMap[string, int]()
// data := []byte(`{"a":1,"b":2,"c":3}`)
// om.UnmarshalJSON(data)
// fmt.Println(om.Elements())
}

View File

@@ -107,14 +107,14 @@ func TestMerge(t *testing.T) {
2: "b",
}
m2 := map[int]string{
1: "1",
3: "2",
2: "c",
3: "d",
}
expected := map[int]string{
1: "1",
2: "b",
3: "2",
1: "a",
2: "c",
3: "d",
}
acturl := Merge(m1, m2)
@@ -707,3 +707,184 @@ func TestGetOrSet(t *testing.T) {
assert.Equal("a", result1)
assert.Equal("b", result2)
}
func TestSortByKey(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSortByKey")
m1 := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
expected1 := map[int]string{
1: "a",
2: "b",
3: "c",
4: "d",
}
result1 := SortByKey(m1, func(a, b int) bool {
return a < b
})
assert.Equal(expected1, result1)
m2 := map[string]int{
"c": 3,
"a": 1,
"d": 4,
"b": 2,
}
expected2 := map[string]int{
"d": 4,
"c": 3,
"b": 2,
"a": 1,
}
result2 := SortByKey(m2, func(a, b string) bool {
return a > b
})
assert.Equal(expected2, result2)
}
type (
Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Address *Address `json:"address"`
}
Address struct {
Street string `json:"street"`
Number int `json:"number"`
}
)
func TestStructType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestStructType")
src := map[string]interface{}{
"name": "Nothin",
"age": 28,
"phone": "123456789",
"address": map[string]interface{}{
"street": "test",
"number": 1,
},
}
var p Person
err := MapTo(src, &p)
assert.IsNil(err)
assert.Equal(src["name"], p.Name)
assert.Equal(src["age"], p.Age)
assert.Equal(src["phone"], p.Phone)
assert.Equal("test", p.Address.Street)
assert.Equal(1, p.Address.Number)
}
func TestBaseType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBaseType")
tc := map[string]interface{}{
"i32": -32,
"i8": -8,
"i16": -16,
"i64": -64,
"i": -1,
"u32": 32,
"u8": 8,
"u16": 16,
"u64": 64,
"u": 1,
"tf": true,
"f32": 1.32,
"f64": 1.64,
"str": "hello mapto",
"complex": 1 + 3i,
}
type BaseType struct {
I int `json:"i"`
I8 int8 `json:"i8"`
I16 int16 `json:"i16"`
I32 int32 `json:"i32"`
I64 int64 `json:"i64"`
U uint `json:"u"`
U8 uint8 `json:"u8"`
U16 uint16 `json:"u16"`
U32 uint32 `json:"u32"`
U64 uint64 `json:"u64"`
F32 float32 `json:"f32"`
F64 float64 `json:"f64"`
Tf bool `json:"tf"`
Str string `json:"str"`
Comp complex128 `json:"complex"`
}
var dist BaseType
err := MapTo(tc, &dist)
assert.IsNil(err)
var number float64
MapTo(tc["i"], &number)
assert.EqualValues(-1, number)
MapTo(tc["i8"], &number)
assert.EqualValues(-8, number)
MapTo(tc["i16"], &number)
assert.EqualValues(-16, number)
MapTo(tc["i32"], &number)
assert.EqualValues(-32, number)
MapTo(tc["i64"], &number)
assert.EqualValues(-64, number)
MapTo(tc["u"], &number)
assert.EqualValues(1, number)
MapTo(tc["u8"], &number)
assert.EqualValues(8, number)
MapTo(tc["u16"], &number)
assert.EqualValues(16, number)
MapTo(tc["u32"], &number)
assert.EqualValues(32, number)
MapTo(tc["u64"], &number)
assert.EqualValues(64, number)
}
func TestGetOrDefault(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "GetOrDefault")
m1 := map[int]string{
3: "c",
1: "a",
4: "d",
2: "b",
}
result1 := GetOrDefault(m1, 1, "123")
assert.Equal("a", result1)
result2 := GetOrDefault(m1, 5, "123")
assert.Equal("123", result2)
}

431
maputil/orderedmap.go Normal file
View File

@@ -0,0 +1,431 @@
package maputil
import (
"container/list"
"encoding/json"
"fmt"
"reflect"
"sort"
"strconv"
"sync"
)
// OrderedMap is a map that maintains the order of keys.
type OrderedMap[K comparable, V any] struct {
mu sync.RWMutex
data map[K]V
order *list.List
index map[K]*list.Element
}
// NewOrderedMap creates a new OrderedMap.
func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V] {
return &OrderedMap[K, V]{
data: make(map[K]V),
order: list.New(),
index: make(map[K]*list.Element),
}
}
// Sets the given key-value pair.
// Play: https://go.dev/play/p/Y4ZJ_oOc1FU
func (om *OrderedMap[K, V]) Set(key K, value V) {
om.mu.Lock()
defer om.mu.Unlock()
if elem, ok := om.index[key]; ok {
om.data[key] = value
om.order.MoveToBack(elem)
return
}
om.data[key] = value
elem := om.order.PushBack(key)
om.index[key] = elem
}
// Get returns the value for the given key.
// Play: https://go.dev/play/p/Y4ZJ_oOc1FU
func (om *OrderedMap[K, V]) Get(key K) (V, bool) {
om.mu.RLock()
defer om.mu.RUnlock()
value, ok := om.data[key]
return value, ok
}
// Delete deletes the given key.
// Play: https://go.dev/play/p/5bIi4yaZ3K-
func (om *OrderedMap[K, V]) Delete(key K) {
om.mu.Lock()
defer om.mu.Unlock()
if elem, ok := om.index[key]; ok {
om.order.Remove(elem)
delete(om.data, key)
delete(om.index, key)
}
}
// Clear clears the map.
// Play: https://go.dev/play/p/8LwoJyEfuFr
func (om *OrderedMap[K, V]) Clear() {
om.mu.Lock()
defer om.mu.Unlock()
om.data = make(map[K]V)
om.order.Init()
om.index = make(map[K]*list.Element)
}
// Front returns the first key-value pair.
// Play: https://go.dev/play/p/ty57XSimpoe
func (om *OrderedMap[K, V]) Front() (struct {
Key K
Value V
}, bool) {
om.mu.RLock()
defer om.mu.RUnlock()
if elem := om.order.Front(); elem != nil {
key := elem.Value.(K)
value := om.data[key]
return struct {
Key K
Value V
}{
Key: key,
Value: value,
}, true
}
return struct {
Key K
Value V
}{}, false
}
// Back returns the last key-value pair.
// Play: https://go.dev/play/p/rQMjp1yQmpa
func (om *OrderedMap[K, V]) Back() (struct {
Key K
Value V
}, bool) {
om.mu.RLock()
defer om.mu.RUnlock()
if elem := om.order.Back(); elem != nil {
key := elem.Value.(K)
value := om.data[key]
return struct {
Key K
Value V
}{
Key: key,
Value: value,
}, true
}
return struct {
Key K
Value V
}{}, false
}
// Range calls the given function for each key-value pair.
// Play: https://go.dev/play/p/U-KpORhc7LZ
func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool) {
om.mu.RLock()
defer om.mu.RUnlock()
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
value := om.data[key]
if !iteratee(key, value) {
break
}
}
}
// Keys returns the keys in order.
// Play: https://go.dev/play/p/Vv_y9ExKclA
func (om *OrderedMap[K, V]) Keys() []K {
om.mu.RLock()
defer om.mu.RUnlock()
keys := make([]K, 0, len(om.data))
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
keys = append(keys, elem.Value.(K))
}
return keys
}
// Values returns the values in order.
// Play: https://go.dev/play/p/TWj5n1-PUfx
func (om *OrderedMap[K, V]) Values() []V {
om.mu.RLock()
defer om.mu.RUnlock()
values := make([]V, 0, len(om.data))
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
values = append(values, om.data[key])
}
return values
}
// Len returns the number of key-value pairs.
// Play: https://go.dev/play/p/cLe6z2VX5N-
func (om *OrderedMap[K, V]) Len() int {
om.mu.RLock()
defer om.mu.RUnlock()
return len(om.data)
}
// Contains returns true if the given key exists.
// Play: https://go.dev/play/p/QuwqqnzwDNX
func (om *OrderedMap[K, V]) Contains(key K) bool {
om.mu.RLock()
defer om.mu.RUnlock()
_, ok := om.data[key]
return ok
}
// Elements returns the key-value pairs in order.
// Play: https://go.dev/play/p/4BHG4kKz6bB
func (om *OrderedMap[K, V]) Elements() []struct {
Key K
Value V
} {
om.mu.RLock()
defer om.mu.RUnlock()
elements := make([]struct {
Key K
Value V
}, 0, len(om.data))
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
value := om.data[key]
elements = append(elements, struct {
Key K
Value V
}{Key: key, Value: value})
}
return elements
}
// Iter returns a channel that yields key-value pairs in order.
// Play: https://go.dev/play/p/tlq2tdvicPt
func (om *OrderedMap[K, V]) Iter() <-chan struct {
Key K
Value V
} {
ch := make(chan struct {
Key K
Value V
})
go func() {
om.mu.RLock()
defer om.mu.RUnlock()
defer close(ch)
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
key := elem.Value.(K)
value := om.data[key]
ch <- struct {
Key K
Value V
}{Key: key, Value: value}
}
}()
return ch
}
// ReverseIter returns a channel that yields key-value pairs in reverse order.
// Play: https://go.dev/play/p/8Q0ssg6hZzO
func (om *OrderedMap[K, V]) ReverseIter() <-chan struct {
Key K
Value V
} {
ch := make(chan struct {
Key K
Value V
})
go func() {
om.mu.RLock()
defer om.mu.RUnlock()
defer close(ch)
for elem := om.order.Back(); elem != nil; elem = elem.Prev() {
key := elem.Value.(K)
value := om.data[key]
ch <- struct {
Key K
Value V
}{Key: key, Value: value}
}
}()
return ch
}
// SortByValue sorts the map by key given less function.
// Play: https://go.dev/play/p/N7hjD_alZPq
func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool) {
om.mu.Lock()
defer om.mu.Unlock()
keys := make([]K, 0, om.order.Len())
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
keys = append(keys, elem.Value.(K))
}
sort.Slice(keys, func(i, j int) bool {
return less(keys[i], keys[j])
})
om.order.Init()
om.index = make(map[K]*list.Element)
for _, key := range keys {
elem := om.order.PushBack(key)
om.index[key] = elem
}
}
// MarshalJSON implements the json.Marshaler interface.
// Play: https://go.dev/play/p/C-wAwydIAC7
func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) {
om.mu.RLock()
defer om.mu.RUnlock()
tempMap := make(map[string]V)
for e := om.order.Front(); e != nil; e = e.Next() {
key := e.Value.(K)
keyStr, err := keyToString(key)
if err != nil {
return nil, err
}
tempMap[keyStr] = om.data[key]
}
return json.Marshal(tempMap)
}
// UnmarshalJSON implements the json.Unmarshaler interface.
// Play: https://go.dev/play/p/8C3MvJ3-mut
func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error {
om.mu.Lock()
defer om.mu.Unlock()
tempMap := make(map[string]V)
if err := json.Unmarshal(data, &tempMap); err != nil {
return err
}
om.data = make(map[K]V)
om.order.Init()
om.index = make(map[K]*list.Element)
for keyStr, value := range tempMap {
key, err := stringToKey[K](keyStr)
if err != nil {
return err
}
om.data[key] = value
elem := om.order.PushBack(key)
om.index[key] = elem
}
return nil
}
func keyToString[K any](key K) (string, error) {
switch v := any(key).(type) {
case int:
return strconv.Itoa(v), nil
case float64:
return strconv.FormatFloat(v, 'f', -1, 64), nil
case string:
return v, nil
default:
// 使用反射将未知类型转换为字符串
rv := reflect.ValueOf(key)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10), nil
case reflect.Float32, reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'f', -1, 64), nil
case reflect.String:
return rv.String(), nil
default:
return "", fmt.Errorf("unsupported key type: %T", key)
}
}
}
func stringToKey[K any](s string) (K, error) {
var zero K
switch any(zero).(type) {
case int:
value, err := strconv.Atoi(s)
return any(value).(K), err
case float64:
value, err := strconv.ParseFloat(s, 64)
return any(value).(K), err
case string:
return any(s).(K), nil
default:
// 使用反射恢复未知类型的键
rv := reflect.ValueOf(&zero).Elem()
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return zero, err
}
rv.SetInt(val)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return zero, err
}
rv.SetUint(val)
case reflect.Float32, reflect.Float64:
val, err := strconv.ParseFloat(s, 64)
if err != nil {
return zero, err
}
rv.SetFloat(val)
case reflect.String:
rv.SetString(s)
default:
return zero, fmt.Errorf("unsupported key type: %T", zero)
}
return rv.Interface().(K), nil
}
}

249
maputil/orderedmap_test.go Normal file
View File

@@ -0,0 +1,249 @@
package maputil
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestOrderedMap_Set_Get(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Set_Get")
val, ok := om.Get("a")
assert.Equal(1, val)
assert.Equal(true, ok)
om.Set("a", 2)
val, _ = om.Get("a")
assert.Equal(2, val)
val, ok = om.Get("d")
assert.Equal(false, ok)
assert.Equal(0, val)
}
func TestOrderedMap_Front_Back(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Front_Back")
frontElement, ok := om.Front()
assert.Equal("a", frontElement.Key)
assert.Equal(1, frontElement.Value)
assert.Equal(true, ok)
backElement, ok := om.Back()
assert.Equal("c", backElement.Key)
assert.Equal(3, backElement.Value)
assert.Equal(true, ok)
}
func TestOrderedMap_Delete_Clear(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Delete_Clear")
assert.Equal(3, om.Len())
om.Delete("b")
assert.Equal(2, om.Len())
om.Clear()
assert.Equal(0, om.Len())
}
func TestOrderedMap_Keys_Values(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Keys_Values")
assert.Equal([]string{"a", "b", "c"}, om.Keys())
assert.Equal([]int{1, 2, 3}, om.Values())
}
func TestOrderedMap_Contains(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Contains")
assert.Equal(true, om.Contains("a"))
assert.Equal(false, om.Contains("d"))
}
func TestOrderedMap_Eelements(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Eelements")
elements := []struct {
Key string
Value int
}{
{"a", 1},
{"b", 2},
{"c", 3},
}
assert.Equal(elements, om.Elements())
}
func TestOrderedMap_Range(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
om.Set("d", 4)
assert := internal.NewAssert(t, "TestOrderedMap_Range")
var keys []string
om.Range(func(key string, value int) bool {
keys = append(keys, key)
return key != "c"
})
assert.Equal([]string{"a", "b", "c"}, keys)
}
func TestOrderedMap_Iter(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_Iter")
var items []struct {
Key string
Value int
}
iterCh := om.Iter()
for item := range iterCh {
items = append(items, item)
}
expected := []struct {
Key string
Value int
}{
{"a", 1},
{"b", 2},
{"c", 3},
}
assert.Equal(expected, items)
}
func TestOrderedMap_ReverseIter(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_ReverseIter")
var items []struct {
Key string
Value int
}
iterCh := om.ReverseIter()
for item := range iterCh {
items = append(items, item)
}
expected := []struct {
Key string
Value int
}{
{"c", 3},
{"b", 2},
{"a", 1},
}
assert.Equal(expected, items)
}
func TestOrderedMap_SortByKey(t *testing.T) {
assert := internal.NewAssert(t, "TestOrderedMap_SortByKey")
om := NewOrderedMap[string, int]()
om.Set("d", 4)
om.Set("b", 2)
om.Set("c", 3)
om.Set("a", 1)
om.SortByKey(func(a, b string) bool {
return a < b
})
assert.Equal([]string{"a", "b", "c", "d"}, om.Keys())
}
func TestOrderedMap_MarshalJSON(t *testing.T) {
om := NewOrderedMap[string, int]()
om.Set("a", 1)
om.Set("b", 2)
om.Set("c", 3)
assert := internal.NewAssert(t, "TestOrderedMap_MarshalJSON")
jsonBytes, err := om.MarshalJSON()
if err != nil {
t.Errorf("MarshalJSON error: %v", err)
}
assert.Equal(`{"a":1,"b":2,"c":3}`, string(jsonBytes))
}
func TestOrderedMap_UnmarshalJSON(t *testing.T) {
assert := internal.NewAssert(t, "TestOrderedMap_UnmarshalJSON")
jsonStr := `{"a":1,"b":2,"c":3}`
om := NewOrderedMap[string, int]()
err := om.UnmarshalJSON([]byte(jsonStr))
if err != nil {
t.Errorf("MarshalJSON error: %v", err)
}
assert.Equal(3, om.Len())
assert.Equal(true, om.Contains("a"))
assert.Equal(true, om.Contains("b"))
assert.Equal(true, om.Contains("c"))
}

View File

@@ -1,191 +0,0 @@
package maputil
import (
"fmt"
"reflect"
"strings"
)
var mapHandlers = map[reflect.Kind]func(reflect.Value, reflect.Value) error{
reflect.String: convertNormal,
reflect.Int: convertNormal,
reflect.Int16: convertNormal,
reflect.Int32: convertNormal,
reflect.Int64: convertNormal,
reflect.Uint: convertNormal,
reflect.Uint16: convertNormal,
reflect.Uint32: convertNormal,
reflect.Uint64: convertNormal,
reflect.Float32: convertNormal,
reflect.Float64: convertNormal,
reflect.Uint8: convertNormal,
reflect.Int8: convertNormal,
reflect.Struct: convertNormal,
reflect.Complex64: convertNormal,
reflect.Complex128: convertNormal,
}
var _ = func() struct{} {
mapHandlers[reflect.Map] = convertMap
mapHandlers[reflect.Array] = convertSlice
mapHandlers[reflect.Slice] = convertSlice
return struct{}{}
}()
// MapTo try to map any interface to struct or base type
/*
Eg:
v := map[string]interface{}{
"service":map[string]interface{}{
"ip":"127.0.0.1",
"port":1234,
},
version:"v1.0.01"
}
type Target struct {
Service struct {
Ip string `json:"ip"`
Port int `json:"port"`
} `json:"service"`
Ver string `json:"version"`
}
var dist Target
if err := maputil.MapTo(v,&dist); err != nil {
log.Println(err)
return
}
log.Println(dist)
*/
// Play: https://go.dev/play/p/4K7KBEPgS5M
func MapTo(src any, dst any) error {
dstRef := reflect.ValueOf(dst)
if dstRef.Kind() != reflect.Ptr {
return fmt.Errorf("dst is not ptr")
}
dstElem := dstRef.Type().Elem()
if dstElem.Kind() == reflect.Struct {
srcMap := src.(map[string]interface{})
return MapToStruct(srcMap, dst)
}
dstRef = reflect.Indirect(dstRef)
srcRef := reflect.ValueOf(src)
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
srcRef = srcRef.Elem()
}
if f, ok := mapHandlers[srcRef.Kind()]; ok {
return f(srcRef, dstRef)
}
return fmt.Errorf("no implemented:%s", srcRef.Type())
}
func convertNormal(src reflect.Value, dst reflect.Value) error {
if dst.CanSet() {
if src.Type() == dst.Type() {
dst.Set(src)
} else if src.CanConvert(dst.Type()) {
dst.Set(src.Convert(dst.Type()))
} else {
return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String())
}
}
return nil
}
func convertSlice(src reflect.Value, dst reflect.Value) error {
if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice {
return fmt.Errorf("error type:%s", dst.Type().String())
}
l := src.Len()
target := reflect.MakeSlice(dst.Type(), l, l)
if dst.CanSet() {
dst.Set(target)
}
for i := 0; i < l; i++ {
srcValue := src.Index(i)
if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface {
srcValue = srcValue.Elem()
}
if f, ok := mapHandlers[srcValue.Kind()]; ok {
err := f(srcValue, dst.Index(i))
if err != nil {
return err
}
}
}
return nil
}
func convertMap(src reflect.Value, dst reflect.Value) error {
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
if src.Kind() == reflect.Interface && dst.IsValid() {
return convertMap(src.Elem(), dst)
} else {
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
}
}
dstType := dst.Type()
num := dstType.NumField()
exist := map[string]int{}
for i := 0; i < num; i++ {
k := dstType.Field(i).Tag.Get("json")
if k == "" {
k = dstType.Field(i).Name
}
if strings.Contains(k, ",") {
taglist := strings.Split(k, ",")
if taglist[0] == "" {
k = dstType.Field(i).Name
} else {
k = taglist[0]
}
}
exist[k] = i
}
keys := src.MapKeys()
for _, key := range keys {
if index, ok := exist[key.String()]; ok {
v := dst.Field(index)
if v.Kind() == reflect.Struct {
err := convertMap(src.MapIndex(key), v)
if err != nil {
return err
}
} else {
if v.CanSet() {
if v.Type() == src.MapIndex(key).Elem().Type() {
v.Set(src.MapIndex(key).Elem())
} else if src.MapIndex(key).Elem().CanConvert(v.Type()) {
v.Set(src.MapIndex(key).Elem().Convert(v.Type()))
} else if f, ok := mapHandlers[src.MapIndex(key).Elem().Kind()]; ok && f != nil {
err := f(src.MapIndex(key).Elem(), v)
if err != nil {
return err
}
} else {
return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type())
}
}
}
}
}
return nil
}

View File

@@ -1,125 +0,0 @@
package maputil
import (
"testing"
"github.com/duke-git/lancet/v2/internal"
)
type (
Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Address *Address `json:"address"`
}
Address struct {
Street string `json:"street"`
Number int `json:"number"`
}
)
func TestStructType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestStructType")
src := map[string]interface{}{
"name": "Nothin",
"age": 28,
"phone": "123456789",
"address": map[string]interface{}{
"street": "test",
"number": 1,
},
}
var p Person
err := MapTo(src, &p)
assert.IsNil(err)
assert.Equal(src["name"], p.Name)
assert.Equal(src["age"], p.Age)
assert.Equal(src["phone"], p.Phone)
assert.Equal("test", p.Address.Street)
assert.Equal(1, p.Address.Number)
}
func TestBaseType(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBaseType")
tc := map[string]interface{}{
"i32": -32,
"i8": -8,
"i16": -16,
"i64": -64,
"i": -1,
"u32": 32,
"u8": 8,
"u16": 16,
"u64": 64,
"u": 1,
"tf": true,
"f32": 1.32,
"f64": 1.64,
"str": "hello mapto",
"complex": 1 + 3i,
}
type BaseType struct {
I int `json:"i"`
I8 int8 `json:"i8"`
I16 int16 `json:"i16"`
I32 int32 `json:"i32"`
I64 int64 `json:"i64"`
U uint `json:"u"`
U8 uint8 `json:"u8"`
U16 uint16 `json:"u16"`
U32 uint32 `json:"u32"`
U64 uint64 `json:"u64"`
F32 float32 `json:"f32"`
F64 float64 `json:"f64"`
Tf bool `json:"tf"`
Str string `json:"str"`
Comp complex128 `json:"complex"`
}
var dist BaseType
err := MapTo(tc, &dist)
assert.IsNil(err)
var number float64
MapTo(tc["i"], &number)
assert.EqualValues(-1, number)
MapTo(tc["i8"], &number)
assert.EqualValues(-8, number)
MapTo(tc["i16"], &number)
assert.EqualValues(-16, number)
MapTo(tc["i32"], &number)
assert.EqualValues(-32, number)
MapTo(tc["i64"], &number)
assert.EqualValues(-64, number)
MapTo(tc["u"], &number)
assert.EqualValues(1, number)
MapTo(tc["u8"], &number)
assert.EqualValues(8, number)
MapTo(tc["u16"], &number)
assert.EqualValues(16, number)
MapTo(tc["u32"], &number)
assert.EqualValues(32, number)
MapTo(tc["u64"], &number)
assert.EqualValues(64, number)
}

View File

@@ -44,14 +44,19 @@ func Fibonacci(first, second, n int) int {
}
}
// Factorial calculate x!.
// Factorial calculate n!.
// Play: https://go.dev/play/p/tt6LdOK67Nx
func Factorial(x uint) uint {
var f uint = 1
for ; x > 1; x-- {
f *= x
func Factorial(n uint) uint {
if n == 0 || n == 1 {
return 1
}
return f
result := uint(1)
for i := uint(2); i <= n; i++ {
result *= i
}
return result
}
// Percent calculate the percentage of value to total.
@@ -224,14 +229,12 @@ func Sum[T constraints.Integer | constraints.Float](numbers ...T) T {
// Average return average value of numbers.
// Play: https://go.dev/play/p/Vv7LBwER-pz
func Average[T constraints.Integer | constraints.Float](numbers ...T) T {
var sum T
n := T(len(numbers))
for _, v := range numbers {
sum += v
func Average[T constraints.Integer | constraints.Float](numbers ...T) float64 {
var sum float64
for _, num := range numbers {
sum += float64(num)
}
return sum / n
return sum / float64(len(numbers))
}
// Range creates a slice of numbers from start with specified count, element step is 1.
@@ -395,3 +398,54 @@ func Abs[T constraints.Integer | constraints.Float](x T) T {
func Div[T constraints.Float | constraints.Integer](x T, y T) float64 {
return float64(x) / float64(y)
}
// Variance returns the variance of numbers.
// Play: todo
func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 {
n := len(numbers)
if n == 0 {
return 0
}
avg := Average(numbers...)
var sum float64
for _, v := range numbers {
sum += (float64(v) - avg) * (float64(v) - avg)
}
return sum / float64(n)
}
// StdDev returns the standard deviation of numbers.
// Play: todo
func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64 {
return math.Sqrt(Variance(numbers))
}
// Permutation calculate P(n, k).
// Play: todo
func Permutation(n, k uint) uint {
if n < k {
return 0
}
nFactorial := Factorial(n)
nMinusKFactorial := Factorial(n - k)
return nFactorial / nMinusKFactorial
}
// Combination calculate C(n, k).
// Play: todo
func Combination(n, k uint) uint {
if n < k {
return 0
}
nFactorial := Factorial(n)
kFactorial := Factorial(k)
nMinusKFactorial := Factorial(n - k)
return nFactorial / (kFactorial * nMinusKFactorial)
}

View File

@@ -178,7 +178,7 @@ func ExampleAverage() {
fmt.Println(result2)
// Output:
// 1
// 1.5
// 1.3
}
@@ -478,3 +478,51 @@ func ExampleDiv() {
// 0.5
// 0
}
func ExampleVariance() {
result1 := Variance([]int{1, 2, 3, 4, 5})
result2 := Variance([]float64{1.1, 2.2, 3.3, 4.4, 5.5})
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 2
// 2.42
}
func ExampleStdDev() {
result1 := TruncRound(StdDev([]int{1, 2, 3, 4, 5}), 2)
result2 := TruncRound(StdDev([]float64{1.1, 2.2, 3.3, 4.4, 5.5}), 2)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 1.41
// 1.55
}
func ExamplePermutation() {
result1 := Permutation(5, 3)
result2 := Permutation(5, 5)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 60
// 120
}
func ExampleCombination() {
result1 := Combination(5, 3)
result2 := Combination(5, 5)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// 10
// 1
}

View File

@@ -143,11 +143,22 @@ func TestAverage(t *testing.T) {
assert := internal.NewAssert(t, "TestAverage")
assert.Equal(0, Average(0, 0))
assert.Equal(1, Average(1, 1))
tests := []struct {
numbers []int
expected float64
}{
{[]int{0}, 0},
{[]int{1, 1, 1}, 1},
{[]int{1, 2, 3, 4}, 2.5},
{[]int{1, 2, 3, 4, 5}, 3},
}
avg := Average(1.2, 1.4)
assert.Equal(1.3, RoundToFloat(avg, 1))
for _, tt := range tests {
assert.Equal(tt.expected, Average(tt.numbers...))
}
avg := Average(1.1, 1.2, 1.3, 1.4)
assert.Equal(1.25, avg)
}
func TestSum(t *testing.T) {
@@ -413,3 +424,116 @@ func TestDiv(t *testing.T) {
assert.Equal(math.Inf(-1), Div(-8, 0))
assert.Equal(true, math.IsNaN(Div(0, 0)))
}
func TestVariance(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestVariance")
testIntNumbers := []struct {
numbers []int
expected float64
}{
{[]int{0}, 0},
{[]int{1, 1, 1}, 0},
{[]int{1, 2, 3, 4}, 1.25},
{[]int{1, 2, 3, 4, 5}, 2.0},
}
for _, tt := range testIntNumbers {
assert.Equal(tt.expected, TruncRound(Variance(tt.numbers), 2))
}
testFloatNumbers := []struct {
numbers []float64
expected float64
}{
{[]float64{0}, 0},
{[]float64{1, 1, 1}, 0},
{[]float64{1.1, 2.2, 3.3, 4.4}, 1.51},
}
for _, tt := range testFloatNumbers {
assert.Equal(tt.expected, TruncRound(Variance(tt.numbers), 2))
}
}
func TestStdDev(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestStdDev")
testIntNumbers := []struct {
numbers []int
expected float64
}{
{[]int{0}, 0},
{[]int{1, 1, 1}, 0},
{[]int{1, 2, 3, 4}, 1.118},
{[]int{1, 2, 3, 4, 5}, 1.414},
}
for _, tt := range testIntNumbers {
assert.Equal(tt.expected, TruncRound(StdDev(tt.numbers), 3))
}
testFloatNumbers := []struct {
numbers []float64
expected float64
}{
{[]float64{0}, 0},
{[]float64{1, 1, 1}, 0},
{[]float64{1.1, 2.2, 3.3, 4.4}, 1.229},
}
for _, tt := range testFloatNumbers {
assert.Equal(tt.expected, TruncRound(StdDev(tt.numbers), 3))
}
}
func TestPermutation(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPermutation")
tests := []struct {
n uint
k uint
expected uint
}{
{1, 1, 1},
{1, 0, 1},
{0, 1, 0},
{0, 0, 1},
{3, 2, 6},
{4, 2, 12},
}
for _, tt := range tests {
assert.Equal(tt.expected, Permutation(tt.n, tt.k))
}
}
func TestCombination(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestCombination")
tests := []struct {
n uint
k uint
expected uint
}{
{1, 1, 1},
{1, 0, 1},
{0, 1, 0},
{0, 0, 1},
{3, 2, 3},
{4, 2, 6},
}
for _, tt := range tests {
assert.Equal(tt.expected, Combination(tt.n, tt.k))
}
}

View File

@@ -221,6 +221,22 @@ func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, err
return resp, nil
}
// AsyncSendRequest send http request with goroutine, pop response and error to channels
func (client *HttpClient) AsyncSendRequest(request *HttpRequest, respChan chan *http.Response, errChan chan error) {
go func() {
defer func() {
if err := recover(); err != nil {
errChan <- fmt.Errorf("%v", err)
}
}()
resp, err := client.SendRequest(request)
if err != nil {
errChan <- err
}
respChan <- resp
}()
}
// DecodeResponse decode response into target object.
// Play: https://go.dev/play/p/jUSgynekH7G
func (client *HttpClient) DecodeResponse(resp *http.Response, target any) error {

View File

@@ -373,6 +373,45 @@ func TestSendRequestWithFilePath(t *testing.T) {
}
}
func TestHttpClient_AsyncSendRequest(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHttpClient_Get")
request := &HttpRequest{
RawURL: "https://jsonplaceholder.typicode.com/todos/1",
Method: "GET",
}
httpClient := NewHttpClient()
respCh := make(chan *http.Response, 20)
errCh := make(chan error, 20)
for i := 0; i < 50; i++ {
httpClient.AsyncSendRequest(request, respCh, errCh)
}
for i := 0; i < 50; i++ {
select {
case resp := <-respCh:
type Todo struct {
UserId int `json:"userId"`
Id int `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
}
var todo Todo
err := httpClient.DecodeResponse(resp, &todo)
if err != nil {
t.FailNow()
}
assert.Equal(1, todo.Id)
case err := <-errCh:
if err != nil {
t.Log("net error: ", err.Error())
}
}
}
}
func TestProxy(t *testing.T) {
config := &HttpClientConfig{
HandshakeTimeout: 20 * time.Second,

View File

@@ -21,21 +21,48 @@ import (
// GetInternalIp return internal ipv4.
// Play: https://go.dev/play/p/5mbu-gFp7ei
func GetInternalIp() string {
addr, err := net.InterfaceAddrs()
addrs, err := net.InterfaceAddrs()
if err != nil {
panic(err.Error())
}
for _, a := range addr {
if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
if ipNet.IP.To4() != nil {
return ipNet.IP.String()
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip != nil && (ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast()) {
continue
}
if ipv4 := ip.To4(); ipv4 != nil {
return ipv4.String()
}
}
return ""
}
// func GetInternalIp() string {
// addr, err := net.InterfaceAddrs()
// if err != nil {
// panic(err.Error())
// }
// for _, a := range addr {
// if ipNet, ok := a.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
// if ipNet.IP.To4() != nil {
// return ipNet.IP.String()
// }
// }
// }
// return ""
// }
// GetRequestPublicIp return the requested public ip.
// Play: https://go.dev/play/p/kxU-YDc_eBo
func GetRequestPublicIp(req *http.Request) string {

View File

@@ -10,6 +10,7 @@ import (
"io"
"math"
"math/rand"
"os"
"time"
"unsafe"
@@ -23,6 +24,7 @@ const (
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
AllChars = Numeral + LowwerLetters + UpperLetters + SymbolChars
)
var rn = rand.NewSource(time.Now().UnixNano())
@@ -31,6 +33,27 @@ func init() {
rand.Seed(time.Now().UnixNano())
}
// RandBool generates a random boolean value (true or false).
// Play: https://go.dev/play/p/to6BLc26wBv
func RandBool() bool {
return rand.Intn(2) == 1
}
// RandBoolSlice generates a random boolean slice of specified length.
// Play: https://go.dev/play/p/o-VSjPjnILI
func RandBoolSlice(length int) []bool {
if length <= 0 {
return []bool{}
}
result := make([]bool, length)
for i := range result {
result[i] = RandBool()
}
return result
}
// RandInt generate random int between [min, max).
// Play: https://go.dev/play/p/pXyyAAI5YxD
func RandInt(min, max int) int {
@@ -42,9 +65,54 @@ func RandInt(min, max int) int {
min, max = max, min
}
if min == 0 && max == math.MaxInt {
return rand.Int()
}
return rand.Intn(max-min) + min
}
// RandIntSlice generates a slice of random integers.
// The generated integers are between min and max (exclusive).
// Play: https://go.dev/play/p/GATTQ5xTEG8
func RandIntSlice(length, min, max int) []int {
if length <= 0 || min > max {
return []int{}
}
result := make([]int, length)
for i := range result {
result[i] = RandInt(min, max)
}
return result
}
// RandUniqueIntSlice generate a slice of random int of length that do not repeat.
// Play: https://go.dev/play/p/uBkRSOz73Ec
func RandUniqueIntSlice(length, min, max int) []int {
if min > max {
return []int{}
}
if length > max-min {
length = max - min
}
nums := make([]int, length)
used := make(map[int]struct{}, length)
for i := 0; i < length; {
r := RandInt(min, max)
if _, use := used[r]; use {
continue
}
used[r] = struct{}{}
nums[i] = r
i++
}
return nums
}
// RandFloat generate random float64 number between [min, max) with specific precision.
// Play: https://go.dev/play/p/zbD_tuobJtr
func RandFloat(min, max float64, precision int) float64 {
@@ -61,6 +129,24 @@ func RandFloat(min, max float64, precision int) float64 {
return mathutil.RoundToFloat(n, precision)
}
// RandFloats generate a slice of random float64 numbers of length that do not repeat.
// Play: https://go.dev/play/p/I3yndUQ-rhh
func RandFloats(length int, min, max float64, precision int) []float64 {
nums := make([]float64, length)
used := make(map[float64]struct{}, length)
for i := 0; i < length; {
r := RandFloat(min, max, precision)
if _, use := used[r]; use {
continue
}
used[r] = struct{}{}
nums[i] = r
i++
}
return nums
}
// RandBytes generate random byte slice.
// Play: https://go.dev/play/p/EkiLESeXf8d
func RandBytes(length int) []byte {
@@ -76,12 +162,69 @@ func RandBytes(length int) []byte {
return b
}
// RandString generate random alpha string of specified length.
// RandString generate random alphabeta string of specified length.
// Play: https://go.dev/play/p/W2xvRUXA7Mi
func RandString(length int) string {
return random(Letters, length)
}
// RandString generate a slice of random string of length strLen based on charset.
// chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters
// random.Letters, random.SymbolChars, random.AllChars. or a combination of them.
// Play: https://go.dev/play/p/2_-PiDv3tGn
func RandStringSlice(charset string, sliceLen, strLen int) []string {
if sliceLen <= 0 || strLen <= 0 {
return []string{}
}
result := make([]string, sliceLen)
for i := range result {
result[i] = random(charset, strLen)
}
return result
}
// RandFromGivenSlice generate a random element from given slice.
// Play: https://go.dev/play/p/UrkWueF6yYo
func RandFromGivenSlice[T any](slice []T) T {
if len(slice) == 0 {
var zero T
return zero
}
return slice[rand.Intn(len(slice))]
}
// RandSliceFromGivenSlice generate a random slice of length num from given slice.
// - If repeatable is true, the generated slice may contain duplicate elements.
//
// Play: https://go.dev/play/p/68UikN9d6VT
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T {
if num <= 0 || len(slice) == 0 {
return slice
}
if !repeatable && num > len(slice) {
num = len(slice)
}
result := make([]T, num)
if repeatable {
for i := range result {
result[i] = slice[rand.Intn(len(slice))]
}
} else {
shuffled := make([]T, len(slice))
copy(shuffled, slice)
rand.Shuffle(len(shuffled), func(i, j int) {
shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
})
result = shuffled[:num]
}
return result
}
// RandUpper generate a random upper case string of specified length.
// Play: https://go.dev/play/p/29QfOh0DVuh
func RandUpper(length int) string {
@@ -131,6 +274,11 @@ func nearestPowerOfTwo(cap int) int {
// random generate a random string based on given string range.
func random(s string, length int) string {
// 确保随机数生成器的种子是动态的
pid := os.Getpid()
timestamp := time.Now().UnixNano()
rand.Seed(int64(pid) + timestamp)
// 仿照strings.Builder
// 创建一个长度为 length 的字节切片
bytes := make([]byte, length)
@@ -186,45 +334,12 @@ func UUIdV4() (string, error) {
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
}
// RandUniqueIntSlice generate a slice of random int of length n that do not repeat.
// Play: https://go.dev/play/p/uBkRSOz73Ec
func RandUniqueIntSlice(n, min, max int) []int {
if min > max {
return []int{}
}
if n > max-min {
n = max - min
}
// RandNumberOfLength 生成一个长度为len的随机数
// Play: todo
func RandNumberOfLength(len int) int {
m := int(math.Pow10(len) - 1)
i := int(math.Pow10(len - 1))
ret := rand.Intn(m-i+1) + i
nums := make([]int, n)
used := make(map[int]struct{}, n)
for i := 0; i < n; {
r := RandInt(min, max)
if _, use := used[r]; use {
continue
}
used[r] = struct{}{}
nums[i] = r
i++
}
return nums
}
// RandFloats generate a slice of random float64 numbers of length n that do not repeat.
// Play: https://go.dev/play/p/I3yndUQ-rhh
func RandFloats(n int, min, max float64, precision int) []float64 {
nums := make([]float64, n)
used := make(map[float64]struct{}, n)
for i := 0; i < n; {
r := RandFloat(min, max, precision)
if _, use := used[r]; use {
continue
}
used[r] = struct{}{}
nums[i] = r
i++
}
return nums
return ret
}

View File

@@ -43,6 +43,21 @@ func ExampleRandString() {
// 6
}
func ExampleRandFromGivenSlice() {
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon",
"mango", "nectarine", "orange"}
result := RandFromGivenSlice(goods)
fmt.Println(result)
}
func ExampleRandSliceFromGivenSlice() {
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon",
"mango", "nectarine", "orange"}
chosen3goods := RandSliceFromGivenSlice(goods, 3, false)
fmt.Println(chosen3goods)
}
func ExampleRandUpper() {
pattern := `^[A-Z]+$`
reg := regexp.MustCompile(pattern)

View File

@@ -3,9 +3,11 @@ package random
import (
"reflect"
"regexp"
"strconv"
"testing"
"github.com/duke-git/lancet/v2/internal"
"github.com/duke-git/lancet/v2/validator"
)
func TestRandString(t *testing.T) {
@@ -196,3 +198,173 @@ func TestRandFloats(t *testing.T) {
assert.Equal(len(numbers), 5)
}
func TestRandIntSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandIntSlice")
t.Run("empty slice", func(t *testing.T) {
numbers := RandIntSlice(-1, 1, 5)
assert.Equal([]int{}, numbers)
numbers = RandIntSlice(0, 1, 5)
assert.Equal([]int{}, numbers)
numbers = RandIntSlice(3, 5, 1)
assert.Equal([]int{}, numbers)
})
t.Run("random int slice", func(t *testing.T) {
numbers := RandIntSlice(5, 1, 1)
assert.Equal([]int{1, 1, 1, 1, 1}, numbers)
numbers = RandIntSlice(5, 1, 5)
assert.Equal(5, len(numbers))
})
}
func TestRandStringSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandStringSlice")
t.Run("empty slice", func(t *testing.T) {
strs := RandStringSlice(Letters, -1, -1)
assert.Equal([]string{}, strs)
strs = RandStringSlice(Letters, 0, 0)
assert.Equal([]string{}, strs)
strs = RandStringSlice(Letters, -1, 0)
assert.Equal([]string{}, strs)
strs = RandStringSlice(Letters, 0, -1)
assert.Equal([]string{}, strs)
strs = RandStringSlice(Letters, 1, 0)
assert.Equal([]string{}, strs)
strs = RandStringSlice(Letters, 0, 1)
assert.Equal([]string{}, strs)
})
t.Run("random string slice", func(t *testing.T) {
strs := RandStringSlice(Letters, 4, 6)
assert.Equal(4, len(strs))
for _, s := range strs {
assert.Equal(true, validator.IsAlpha(s))
assert.Equal(6, len(s))
}
})
// fail test: chinese character is not supported for now
// t.Run("random string slice of chinese ", func(t *testing.T) {
// strs := RandStringSlice("你好你好你好你好你好你好你好你好你好", 4, 6)
// t.Log(strs)
// assert.Equal(4, len(strs))
// for _, s := range strs {
// assert.Equal(true, validator.ContainChinese(s))
// assert.Equal(6, len(s))
// }
// })
}
func TestRandFromGivenSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandFromGivenSlice")
randomSet := []any{"a", 8, "王", true, 1.1}
result := RandFromGivenSlice(randomSet)
find := false
for _, v := range randomSet {
if v == result {
find = true
}
}
assert.Equal(true, find)
emptyAnyRandomSet := []any{}
emptyAnyResult := RandFromGivenSlice(emptyAnyRandomSet)
assert.IsNil(emptyAnyResult)
emptyIntRandomSet := []int{}
emtpyIntResult := RandFromGivenSlice(emptyIntRandomSet)
assert.Equal(0, emtpyIntResult)
}
func TestRandSliceFromGivenSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandSliceFromGivenSlice")
randomSet := []any{"a", 8, "王", true, 1.1}
repeatableResult := RandSliceFromGivenSlice(randomSet, 8, true)
assert.Equal(8, len(repeatableResult))
unrepeatableResult := RandSliceFromGivenSlice(randomSet, 8, false)
assert.Equal(len(randomSet), len(unrepeatableResult))
var findCount int
for _, v := range repeatableResult {
for _, vv := range randomSet {
if v == vv {
findCount++
}
}
}
assert.Equal(8, findCount)
findCount = 0
for _, v := range unrepeatableResult {
for _, vv := range randomSet {
if v == vv {
findCount++
}
}
}
assert.Equal(len(randomSet), findCount)
emptyAnyRandomSet := []any{}
emptyAnyResult := RandSliceFromGivenSlice(emptyAnyRandomSet, 3, true)
assert.Equal([]any{}, emptyAnyResult)
emptyIntRandomSet := []int{}
emtpyIntResult := RandSliceFromGivenSlice(emptyIntRandomSet, 3, true)
assert.Equal([]int{}, emtpyIntResult)
}
func TestRandBool(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandBool")
result := RandBool()
assert.Equal(true, result == true || result == false)
}
func TestRandBoolSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandBoolSlice")
t.Run("empty slice", func(t *testing.T) {
bools := RandBoolSlice(-1)
assert.Equal([]bool{}, bools)
bools = RandBoolSlice(0)
assert.Equal([]bool{}, bools)
})
t.Run("random bool slice", func(t *testing.T) {
bools := RandBoolSlice(6)
assert.Equal(6, len(bools))
for _, b := range bools {
assert.Equal(true, b == true || b == false)
}
})
}
func TestRandNumberOfLength(t *testing.T) {
t.Parallel()
randi := RandNumberOfLength(6)
assert := internal.NewAssert(t, "TestRandNumberOfLength")
assert.Equal(6, len(strconv.Itoa(randi)))
}

View File

@@ -45,7 +45,7 @@ func RetryTimes(n uint) Option {
}
// RetryWithCustomBackoff set abitary custom backoff strategy
// Play: todo
// Play: https://go.dev/play/p/jIm_o2vb5Y4
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
if backoffStrategy == nil {
panic("programming error: backoffStrategy must be not nil")
@@ -57,7 +57,7 @@ func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
}
// RetryWithLinearBackoff set linear strategy backoff
// Play: todo
// Play: https://go.dev/play/p/PDet2ZQZwcB
func RetryWithLinearBackoff(interval time.Duration) Option {
if interval <= 0 {
panic("programming error: retry interval should not be lower or equal to 0")
@@ -71,7 +71,7 @@ func RetryWithLinearBackoff(interval time.Duration) Option {
}
// RetryWithExponentialWithJitterBackoff set exponential strategy backoff
// Play: todo
// Play: https://go.dev/play/p/xp1avQmn16X
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option {
if interval <= 0 {
panic("programming error: retry interval should not be lower or equal to 0")

View File

@@ -10,6 +10,7 @@ import (
"reflect"
"sort"
"strings"
"sync"
"time"
"github.com/duke-git/lancet/v2/random"
@@ -21,6 +22,7 @@ import (
var (
memoryHashMap = make(map[string]map[any]int)
memoryHashCounter = make(map[string]int)
muForMemoryHash sync.RWMutex
)
// Contain check if the target value is in the slice or not.
@@ -771,33 +773,69 @@ func UpdateAt[T any](slice []T, index int, value T) []T {
// Unique remove duplicate elements in slice.
// Play: https://go.dev/play/p/AXw0R3ZTE6a
func Unique[T comparable](slice []T) []T {
result := []T{}
exists := map[T]bool{}
for _, t := range slice {
if exists[t] {
result := make([]T, 0, len(slice))
seen := make(map[T]struct{}, len(slice))
for i := range slice {
if _, ok := seen[slice[i]]; ok {
continue
}
exists[t] = true
result = append(result, t)
seen[slice[i]] = struct{}{}
result = append(result, slice[i])
}
return result
}
// UniqueBy call iteratee func with every item of slice, then remove duplicated.
// Play: https://go.dev/play/p/UR323iZLDpv
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T {
result := []T{}
// UniqueBy removes duplicate elements from the input slice based on the values returned by the iteratee function.
// The function maintains the order of the elements.
// Play: https://go.dev/play/p/GY7JE4yikrl
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T {
result := make([]T, 0, len(slice))
seen := make(map[U]struct{}, len(slice))
for _, v := range slice {
val := iteratee(v)
result = append(result, val)
for i := range slice {
key := iteratee(slice[i])
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
result = append(result, slice[i])
}
return Unique(result)
return result
}
// UniqueByComparator removes duplicate elements from the input slice using the provided comparator function.
// The function maintains the order of the elements.
// Play: https://go.dev/play/p/rwSacr-ZHsR
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T {
result := make([]T, 0, len(slice))
seen := make([]T, 0, len(slice))
for _, item := range slice {
duplicate := false
for _, seenItem := range seen {
if comparator(item, seenItem) {
duplicate = true
break
}
}
if !duplicate {
seen = append(seen, item)
result = append(result, item)
}
}
return result
}
// UniqueByField remove duplicate elements in struct slice by struct field.
// Play: todo
// Play: https://go.dev/play/p/6cifcZSPIGu
func UniqueByField[T any](slice []T, field string) ([]T, error) {
seen := map[any]struct{}{}
@@ -1138,23 +1176,46 @@ func IndexOf[T comparable](arr []T, val T) int {
limit := 10
// gets the hash value of the array as the key of the hash table.
key := fmt.Sprintf("%p", arr)
muForMemoryHash.RLock()
// determines whether the hash table is empty. If so, the hash table is created.
if memoryHashMap[key] == nil {
memoryHashMap[key] = make(map[any]int)
// iterate through the array, adding the value and index of each element to the hash table.
for i := len(arr) - 1; i >= 0; i-- {
memoryHashMap[key][arr[i]] = i
muForMemoryHash.RUnlock()
muForMemoryHash.Lock()
if memoryHashMap[key] == nil {
memoryHashMap[key] = make(map[any]int)
// iterate through the array, adding the value and index of each element to the hash table.
for i := len(arr) - 1; i >= 0; i-- {
memoryHashMap[key][arr[i]] = i
}
}
muForMemoryHash.Unlock()
} else {
muForMemoryHash.RUnlock()
}
muForMemoryHash.Lock()
// update the hash table counter.
memoryHashCounter[key]++
muForMemoryHash.Unlock()
// use the hash table to find the specified value. If found, the index is returned.
if index, ok := memoryHashMap[key][val]; ok {
muForMemoryHash.RLock()
index, ok := memoryHashMap[key][val]
muForMemoryHash.RUnlock()
if ok {
muForMemoryHash.RLock()
// calculate the memory usage of the hash table.
size := len(memoryHashMap)
muForMemoryHash.RUnlock()
// If the memory usage of the hash table exceeds the memory limit, the hash table with the lowest counter is cleared.
if size > limit {
muForMemoryHash.Lock()
var minKey string
var minVal int
for k, v := range memoryHashCounter {
@@ -1168,6 +1229,7 @@ func IndexOf[T comparable](arr []T, val T) int {
}
delete(memoryHashMap, minKey)
delete(memoryHashCounter, minKey)
muForMemoryHash.Unlock()
}
return index
}
@@ -1350,3 +1412,47 @@ func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
return paddedSlice
}
// Frequency counts the frequency of each element in the slice.
// Play: https://go.dev/play/p/CW3UVNdUZOq
func Frequency[T comparable](slice []T) map[T]int {
result := make(map[T]int)
for _, v := range slice {
result[v]++
}
return result
}
// JoinFunc joins the slice elements into a single string with the given separator.
// Play: todo
func JoinFunc[T any](slice []T, sep string, transform func(T) T) string {
var buf strings.Builder
for i, v := range slice {
if i > 0 {
buf.WriteString(sep)
}
buf.WriteString(fmt.Sprint(transform(v)))
}
return buf.String()
}
// ConcatBy concats the elements of a slice into a single value using the provided separator and connector function.
// Play: todo
func ConcatBy[T any](slice []T, sep T, connector func(T, T) T) T {
var result T
if len(slice) == 0 {
return result
}
for i, v := range slice {
result = connector(result, v)
if i < len(slice)-1 {
result = connector(result, sep)
}
}
return result
}

233
slice/slice_concurrent.go Normal file
View File

@@ -0,0 +1,233 @@
// Copyright 2024 dudaodong@gmail.com. All rights resulterved.
// Use of this source code is governed by MIT license
package slice
import (
"runtime"
"sync"
)
// ForEachConcurrent applies the iteratee function to each item in the slice concurrently.
// Play: https://go.dev/play/p/kT4XW7DKVoV
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int) {
sliceLen := len(slice)
if sliceLen == 0 {
return
}
if numThreads <= 0 {
numThreads = 1
}
var wg sync.WaitGroup
chunkSize := (sliceLen + numThreads - 1) / numThreads
for i := 0; i < numThreads; i++ {
start := i * chunkSize
end := start + chunkSize
if start >= sliceLen {
break
}
if end > sliceLen {
end = sliceLen
}
wg.Add(1)
go func(start, end int) {
defer wg.Done()
for j := start; j < end; j++ {
iteratee(j, slice[j])
}
}(start, end)
}
wg.Wait()
}
// MapConcurrent applies the iteratee function to each item in the slice concurrently.
// Play: https://go.dev/play/p/H1ehfPkPen0
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U {
result := make([]U, len(slice))
var wg sync.WaitGroup
workerChan := make(chan struct{}, numThreads)
for index, item := range slice {
wg.Add(1)
workerChan <- struct{}{}
go func(i int, v T) {
defer wg.Done()
result[i] = iteratee(i, v)
<-workerChan
}(index, item)
}
wg.Wait()
return result
}
// ReduceConcurrent reduces the slice to a single value by applying the reducer function to each item in the slice concurrently.
// Play: https://go.dev/play/p/Tjwe6OtaG07
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T {
if numThreads <= 0 {
numThreads = 1
}
var wg sync.WaitGroup
var mu sync.Mutex
sliceLen := len(slice)
chunkSize := (sliceLen + numThreads - 1) / numThreads
results := make([]T, numThreads)
for i := 0; i < numThreads; i++ {
start := i * chunkSize
end := start + chunkSize
if end > sliceLen {
end = sliceLen
}
wg.Add(1)
go func(i, start, end int) {
defer wg.Done()
tempResult := initial
for j := start; j < end; j++ {
tempResult = reducer(j, slice[j], tempResult)
}
mu.Lock()
results[i] = tempResult
mu.Unlock()
}(i, start, end)
}
wg.Wait()
result := initial
for i, r := range results {
result = reducer(i, result, r)
}
return result
}
// FilterConcurrent applies the provided filter function `predicate` to each element of the input slice concurrently.
// Play: https://go.dev/play/p/t_pkwerIRVx
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T {
result := make([]T, 0)
var wg sync.WaitGroup
workerChan := make(chan struct{}, numThreads)
for index, item := range slice {
wg.Add(1)
workerChan <- struct{}{}
go func(i int, v T) {
defer wg.Done()
if predicate(i, v) {
result = append(result, v)
}
<-workerChan
}(index, item)
}
wg.Wait()
return result
}
// UniqueByConcurrent removes duplicate elements from the slice by parallel
// The comparator function is used to compare the elements
// The numThreads parameter specifies the number of threads to use
// If numThreads is less than or equal to 0, it will be set to 1
// The comparator function should return true if the two elements are equal
// Play: https://go.dev/play/p/wXZ7LcYRMGL
func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T {
if numThreads <= 0 {
numThreads = 1
} else if numThreads > len(slice) {
numThreads = len(slice)
}
maxThreads := runtime.NumCPU()
if numThreads > maxThreads {
numThreads = maxThreads
}
removeDuplicate := func(items []T, comparator func(item T, other T) bool) []T {
var result []T
for _, item := range items {
seen := false
for _, r := range result {
if comparator(item, r) {
seen = true
break
}
}
if !seen {
result = append(result, item)
}
}
return result
}
chunkSize := (len(slice) + numThreads - 1) / numThreads
chunks := make([][]T, 0, numThreads)
for i := 0; i < len(slice); i += chunkSize {
end := i + chunkSize
if end > len(slice) {
end = len(slice)
}
chunks = append(chunks, slice[i:end])
}
resultCh := make(chan resultChunk[T], numThreads)
var wg sync.WaitGroup
for i, chunk := range chunks {
wg.Add(1)
go func(index int, chunk []T) {
defer wg.Done()
resultCh <- resultChunk[T]{index, removeDuplicate(chunk, comparator)}
}(i, chunk)
}
go func() {
wg.Wait()
close(resultCh)
}()
results := make([][]T, len(chunks))
for r := range resultCh {
results[r.index] = r.data
}
result := []T{}
seen := make(map[T]bool)
for _, chunk := range results {
for _, item := range chunk {
if !seen[item] {
seen[item] = true
result = append(result, item)
}
}
}
return result
}

View File

@@ -5,6 +5,7 @@ import (
"math"
"reflect"
"strconv"
"strings"
)
func ExampleContain() {
@@ -246,6 +247,21 @@ func ExampleFilter() {
// [2 4]
}
func ExampleFilterConcurrent() {
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
result := FilterConcurrent(nums, isEven, 2)
fmt.Println(result)
// Output:
// [2 4]
}
func ExampleCount() {
nums := []int{1, 2, 3, 3, 4}
@@ -413,6 +429,23 @@ func ExampleForEach() {
// [2 3 4]
}
func ExampleForEachConcurrent() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
result := make([]int, len(nums))
addOne := func(index int, value int) {
result[index] = value + 1
}
ForEachConcurrent(nums, addOne, 4)
fmt.Println(result)
// Output:
// [2 3 4 5 6 7 8 9]
}
func ExampleForEachWithBreak() {
numbers := []int{1, 2, 3, 4, 5}
@@ -494,6 +527,18 @@ func ExampleReduce() {
// 6
}
func ExampleReduceConcurrent() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 1)
fmt.Println(result)
// Output:
// 55
}
func ExampleReduceBy() {
result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int {
return agg + item
@@ -777,7 +822,24 @@ func ExampleUniqueBy() {
fmt.Println(result)
// Output:
// [1 2 0]
// [1 2 3]
}
func ExampleUniqueByComparator() {
uniqueNums := UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
return item == other
})
caseInsensitiveStrings := UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool {
return strings.ToLower(item) == strings.ToLower(other)
})
fmt.Println(uniqueNums)
fmt.Println(caseInsensitiveStrings)
// Output:
// [1 2 3 4 5 6]
// [apple banana cherry date]
}
func ExampleUniqueByField() {
@@ -1121,6 +1183,7 @@ func ExampleRandom() {
if idx >= 0 && idx < len(nums) && Contain(nums, val) {
fmt.Println("okk")
}
// Output:
// okk
}
@@ -1130,6 +1193,7 @@ func ExampleSetToDefaultIf() {
modifiedStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
fmt.Println(modifiedStrs)
fmt.Println(count)
// Output:
// [ b c d ]
// 3
@@ -1152,6 +1216,7 @@ func ExampleRightPadding() {
nums := []int{1, 2, 3, 4, 5}
padded := RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
}
@@ -1160,6 +1225,78 @@ func ExampleLeftPadding() {
nums := []int{1, 2, 3, 4, 5}
padded := LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
func ExampleUniqueByConcurrent() {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool { return item == other }
result := UniqueByConcurrent(nums, comparator, 4)
fmt.Println(result)
// Output:
// [1 2 3 4 5 6 7]
}
func ExampleMapConcurrent() {
nums := []int{1, 2, 3, 4, 5, 6}
result := MapConcurrent(nums, func(_, n int) int { return n * n }, 4)
fmt.Println(result)
// Output:
// [1 4 9 16 25 36]
}
func ExampleFrequency() {
strs := []string{"a", "b", "b", "c", "c", "c"}
result := Frequency(strs)
fmt.Println(result)
// Output:
// map[a:1 b:2 c:3]
}
func ExampleJoinFunc() {
result := JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string {
return strings.ToUpper(s)
})
fmt.Println(result)
// Output:
// A, B, C
}
func ExampleConcatBy() {
type Person struct {
Name string
Age int
}
people := []Person{
{Name: "Alice", Age: 30},
{Name: "Bob", Age: 25},
{Name: "Charlie", Age: 35},
}
sep := Person{Name: " | ", Age: 0}
personConnector := func(a, b Person) Person {
return Person{Name: a.Name + b.Name, Age: a.Age + b.Age}
}
result := ConcatBy(people, sep, personConnector)
fmt.Println(result.Name)
fmt.Println(result.Age)
// Output:
// Alice | Bob | Charlie
// 90
}

View File

@@ -7,6 +7,13 @@ import (
"golang.org/x/exp/constraints"
)
// resultChunk is used to store the intermediate results of UniqueByConcurrent.
// It is defined separately to be compatible with versions of go up to 1.20.
type resultChunk[T comparable] struct {
index int
data []T
}
// sliceValue return the reflect value of a slice
func sliceValue(slice any) reflect.Value {
v := reflect.ValueOf(slice)

View File

@@ -5,6 +5,8 @@ import (
"math"
"reflect"
"strconv"
"strings"
"sync"
"testing"
"github.com/duke-git/lancet/v2/internal"
@@ -15,12 +17,20 @@ func TestContain(t *testing.T) {
assert := internal.NewAssert(t, "TestContain")
assert.Equal(true, Contain([]string{"a", "b", "c"}, "a"))
assert.Equal(false, Contain([]string{"a", "b", "c"}, "d"))
assert.Equal(true, Contain([]string{""}, ""))
assert.Equal(false, Contain([]string{}, ""))
tests := []struct {
slice []string
give string
want bool
}{
{[]string{"a", "b", "c"}, "a", true},
{[]string{"a", "b", "c"}, "d", false},
{[]string{""}, "", true},
{[]string{}, "", false},
}
assert.Equal(true, Contain([]int{1, 2, 3}, 1))
for _, tt := range tests {
assert.Equal(tt.want, Contain(tt.slice, tt.give))
}
}
func TestContainBy(t *testing.T) {
@@ -29,32 +39,53 @@ func TestContainBy(t *testing.T) {
assert := internal.NewAssert(t, "TestContainBy")
type foo struct {
A string
B int
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 })
tests := []struct {
slice []foo
predicateFn func(f foo) bool
want bool
}{
{
[]foo{{a: "1", b: 1}, {a: "2", b: 2}},
func(f foo) bool { return f.a == "1" && f.b == 1 },
true,
},
{
[]foo{{a: "1", b: 1}, {a: "2", b: 2}},
func(f foo) bool { return f.a == "2" && f.b == 1 },
false,
},
}
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)
for _, tt := range tests {
assert.Equal(tt.want, ContainBy(tt.slice, tt.predicateFn))
}
}
func TestContainSubSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestContainSubSlice")
assert.Equal(true, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "a"}))
assert.Equal(false, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "d"}))
assert.Equal(true, ContainSubSlice([]int{1, 2, 3}, []int{1, 2}))
assert.Equal(false, ContainSubSlice([]int{1, 2, 3}, []int{0, 1}))
tests := []struct {
slice []string
subSlice []string
want bool
}{
{[]string{"a", "b", "c"}, []string{"a", "b"}, true},
{[]string{"a", "b", "c"}, []string{"a", "d"}, false},
{[]string{"a", "b", "c"}, []string{"a", "b", "c"}, true},
{[]string{"a", "b", "c"}, []string{"a", "b", "c", "d"}, false},
{[]string{"a", "b", ""}, []string{"a", ""}, true},
{[]string{""}, []string{""}, true},
}
for _, tt := range tests {
assert.Equal(tt.want, ContainSubSlice(tt.slice, tt.subSlice))
}
}
func TestChunk(t *testing.T) {
@@ -62,29 +93,24 @@ func TestChunk(t *testing.T) {
assert := internal.NewAssert(t, "TestChunk")
arr := []string{"a", "b", "c", "d", "e"}
tests := []struct {
slice []string
chuanSize int
want [][]string
}{
{[]string{"a", "b", "c", "d", "e"}, -1, [][]string{}},
{[]string{"a", "b", "c", "d", "e"}, 0, [][]string{}},
{[]string{"a", "b", "c", "d", "e"}, 1, [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}},
{[]string{"a", "b", "c", "d", "e"}, 2, [][]string{{"a", "b"}, {"c", "d"}, {"e"}}},
{[]string{"a", "b", "c", "d", "e"}, 3, [][]string{{"a", "b", "c"}, {"d", "e"}}},
{[]string{"a", "b", "c", "d", "e"}, 4, [][]string{{"a", "b", "c", "d"}, {"e"}}},
{[]string{"a", "b", "c", "d", "e"}, 5, [][]string{{"a", "b", "c", "d", "e"}}},
{[]string{"a", "b", "c", "d", "e"}, 6, [][]string{{"a", "b", "c", "d", "e"}}},
}
assert.Equal([][]string{}, Chunk(arr, -1))
assert.Equal([][]string{}, Chunk(arr, 0))
r1 := [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
assert.Equal(r1, Chunk(arr, 1))
r2 := [][]string{{"a", "b"}, {"c", "d"}, {"e"}}
assert.Equal(r2, Chunk(arr, 2))
r3 := [][]string{{"a", "b", "c"}, {"d", "e"}}
assert.Equal(r3, Chunk(arr, 3))
r4 := [][]string{{"a", "b", "c", "d"}, {"e"}}
assert.Equal(r4, Chunk(arr, 4))
r5 := [][]string{{"a", "b", "c", "d", "e"}}
assert.Equal(r5, Chunk(arr, 5))
r6 := [][]string{{"a", "b", "c", "d", "e"}}
assert.Equal(r6, Chunk(arr, 6))
for _, tt := range tests {
assert.Equal(tt.want, Chunk(tt.slice, tt.chuanSize))
}
}
func TestCompact(t *testing.T) {
@@ -133,6 +159,7 @@ func TestEqual(t *testing.T) {
assert.Equal(true, Equal(slice1, slice2))
assert.Equal(false, Equal(slice1, slice3))
assert.Equal(false, Equal(slice2, slice3))
}
// go test -fuzz=Fuzz -fuzztime=10s .
@@ -162,12 +189,27 @@ func TestEvery(t *testing.T) {
assert := internal.NewAssert(t, "TestEvery")
nums := []int{1, 2, 3, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
isOdd := func(i, num int) bool {
return num%2 == 1
}
assert.Equal(false, Every(nums, isEven))
tests := []struct {
slice []int
predicateFn func(i, num int) bool
want bool
}{
{[]int{1, 3, 5, 7}, isOdd, true},
{[]int{2, 4, 6, 8}, isEven, true},
{[]int{1, 2, 3, 4}, isOdd, false},
{[]int{1, 2, 3, 4}, isEven, false},
}
for _, tt := range tests {
assert.Equal(tt.want, Every(tt.slice, tt.predicateFn))
}
}
func TestNone(t *testing.T) {
@@ -175,12 +217,27 @@ func TestNone(t *testing.T) {
assert := internal.NewAssert(t, "TestNone")
nums := []int{1, 2, 3, 5}
check := func(i, num int) bool {
isEven := func(i, num int) bool {
return num%2 == 0
}
isOdd := func(i, num int) bool {
return num%2 == 1
}
assert.Equal(false, None(nums, check))
tests := []struct {
slice []int
predicateFn func(i, num int) bool
want bool
}{
{[]int{1, 3, 5, 7}, isEven, true},
{[]int{2, 4, 6, 8}, isOdd, true},
{[]int{1, 2, 3, 4}, isOdd, false},
{[]int{1, 2, 3, 4}, isEven, false},
}
for _, tt := range tests {
assert.Equal(tt.want, None(tt.slice, tt.predicateFn))
}
}
func TestSome(t *testing.T) {
@@ -188,12 +245,27 @@ func TestSome(t *testing.T) {
assert := internal.NewAssert(t, "TestSome")
nums := []int{1, 2, 3, 5}
hasEven := func(i, num int) bool {
isEven := func(i, num int) bool {
return num%2 == 0
}
isOdd := func(i, num int) bool {
return num%2 == 1
}
assert.Equal(true, Some(nums, hasEven))
tests := []struct {
slice []int
predicateFn func(i, num int) bool
want bool
}{
{[]int{1, 3, 5, 7}, isEven, false},
{[]int{2, 4, 6, 8}, isOdd, false},
{[]int{1, 2, 3, 4}, isOdd, true},
{[]int{1, 2, 3, 4}, isEven, true},
}
for _, tt := range tests {
assert.Equal(tt.want, Some(tt.slice, tt.predicateFn))
}
}
func TestFilter(t *testing.T) {
@@ -201,33 +273,37 @@ func TestFilter(t *testing.T) {
assert := internal.NewAssert(t, "TestFilter")
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
t.Run("filter int slice", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5}
isEven := func(i, num int) bool {
return num%2 == 0
}
assert.Equal([]int{2, 4}, Filter(nums, isEven))
assert.Equal([]int{2, 4}, Filter(nums, isEven))
})
type student struct {
name string
age int
}
students := []student{
{"a", 10},
{"b", 11},
{"c", 12},
{"d", 13},
{"e", 14},
}
studentsOfAageGreat12 := []student{
{"d", 13},
{"e", 14},
}
filterFunc := func(i int, s student) bool {
return s.age > 12
}
t.Run("filter struct slice", func(t *testing.T) {
type student struct {
name string
age int
}
students := []student{
{"a", 10},
{"b", 11},
{"c", 12},
{"d", 13},
{"e", 14},
}
studentsOfAgeGreat12 := []student{
{"d", 13},
{"e", 14},
}
filterFunc := func(i int, s student) bool {
return s.age > 12
}
assert.Equal(studentsOfAageGreat12, Filter(students, filterFunc))
assert.Equal(studentsOfAgeGreat12, Filter(students, filterFunc))
})
}
func TestGroupBy(t *testing.T) {
@@ -248,6 +324,8 @@ func TestGroupBy(t *testing.T) {
func TestGroupWith(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestGroupWith")
nums := []float64{6.1, 4.2, 6.3}
floor := func(num float64) float64 {
return math.Floor(num)
@@ -256,9 +334,8 @@ func TestGroupWith(t *testing.T) {
4: {4.2},
6: {6.1, 6.3},
}
actual := GroupWith(nums, floor)
assert := internal.NewAssert(t, "TestGroupWith")
assert.Equal(expected, actual)
assert.Equal(expected, GroupWith(nums, floor))
}
func TestCount(t *testing.T) {
@@ -294,8 +371,15 @@ func TestFind(t *testing.T) {
result, ok := Find(nums, even)
assert := internal.NewAssert(t, "TestFind")
assert.Equal(true, ok)
assert.Equal(2, *result)
_, ok = Find(nums, func(_ int, v int) bool {
return v == 6
})
assert.Equal(false, ok)
}
func TestFindBy(t *testing.T) {
@@ -357,19 +441,6 @@ func TestFindLast(t *testing.T) {
assert.Equal(4, *result)
}
func TestFindFoundNothing(t *testing.T) {
t.Parallel()
nums := []int{1, 1, 1, 1, 1, 1}
findFunc := func(i, num int) bool {
return num > 1
}
_, ok := Find(nums, findFunc)
assert := internal.NewAssert(t, "TestFindFoundNothing")
assert.Equal(false, ok)
}
func TestFlatten(t *testing.T) {
t.Parallel()
@@ -409,6 +480,73 @@ func TestForEach(t *testing.T) {
assert.Equal([]int{3, 4, 5, 6, 7}, result)
}
func TestForEachConcurrent(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestForEachConcurrent")
t.Run("single thread", func(t *testing.T) {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
result := make([]int, len(numbers))
addOne := func(index int, value int) {
result[index] = value + 1
}
ForEachConcurrent(numbers, addOne, 1)
expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10}
assert.Equal(expected, result)
})
t.Run("normal", func(t *testing.T) {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
result := make([]int, len(numbers))
addOne := func(index int, value int) {
result[index] = value + 1
}
ForEachConcurrent(numbers, addOne, 4)
expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10}
assert.Equal(expected, result)
})
t.Run("negative threads", func(t *testing.T) {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
result := make([]int, len(numbers))
addOne := func(index int, value int) {
result[index] = value + 1
}
ForEachConcurrent(numbers, addOne, -4)
expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10}
assert.Equal(expected, result)
})
t.Run("high number threads", func(t *testing.T) {
numbers := make([]int, 1000)
for i := range numbers {
numbers[i] = i
}
result := make([]int, len(numbers))
addOne := func(index int, value int) {
result[index] = value + 1
}
ForEachConcurrent(numbers, addOne, 50)
for i, item := range numbers {
assert.Equal(item+1, result[i])
}
})
}
func TestForEachWithBreak(t *testing.T) {
t.Parallel()
@@ -519,6 +657,44 @@ func TestReduce(t *testing.T) {
}
}
func TestReduceConcurrent(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestReduceConcurrent")
t.Run("basic", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 4)
assert.Equal(55, result)
})
t.Run("empty slice", func(t *testing.T) {
nums := []int{}
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 4)
assert.Equal(0, result)
})
t.Run("single thread", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, 1)
assert.Equal(55, result)
})
t.Run("negative threads", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
return agg + item
}, -1)
assert.Equal(55, result)
})
}
func TestReduceBy(t *testing.T) {
t.Parallel()
@@ -527,14 +703,12 @@ func TestReduceBy(t *testing.T) {
result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int {
return agg + item
})
assert.Equal(10, result1)
result2 := ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string {
return agg + fmt.Sprintf("%v", item)
})
assert.Equal(10, result1)
assert.Equal("1234", result2)
}
func TestReduceRight(t *testing.T) {
@@ -586,15 +760,23 @@ func TestDeleteAt(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestDeleteAt")
arr := []int{1, 2, 3, 4, 5}
assert.Equal([]int{2, 3, 4, 5}, DeleteAt(arr, 0))
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 4))
tests := []struct {
slice []int
deletePos int
wang []int
}{
{[]int{1, 2, 3, 4, 5}, 0, []int{2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 1, []int{1, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 2, []int{1, 2, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 3, []int{1, 2, 3, 5}},
{[]int{1, 2, 3, 4, 5}, 4, []int{1, 2, 3, 4}},
{[]int{1, 2, 3, 4, 5}, 5, []int{1, 2, 3, 4}},
}
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 5))
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 6))
assert.Equal([]int{1, 2, 3, 4, 5}, arr)
for _, tt := range tests {
assert.Equal(tt.wang, DeleteAt(tt.slice, tt.deletePos))
}
}
func TestDeleteRange(t *testing.T) {
@@ -614,16 +796,24 @@ func TestDrop(t *testing.T) {
assert := internal.NewAssert(t, "TestDrop")
assert.Equal([]int{}, Drop([]int{}, 0))
assert.Equal([]int{}, Drop([]int{}, 1))
assert.Equal([]int{}, Drop([]int{}, -1))
tests := []struct {
slice []int
dropNum int
want []int
}{
{[]int{}, 0, []int{}},
{[]int{}, 1, []int{}},
{[]int{}, -1, []int{}},
{[]int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 0, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 1, []int{2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 5, []int{}},
{[]int{1, 2, 3, 4, 5}, 6, []int{}},
}
assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 0))
assert.Equal([]int{2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 1))
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 5))
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 6))
assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, -1))
for _, tt := range tests {
assert.Equal(tt.want, Drop(tt.slice, tt.dropNum))
}
}
func TestDropRight(t *testing.T) {
@@ -631,16 +821,23 @@ func TestDropRight(t *testing.T) {
assert := internal.NewAssert(t, "TestDropRight")
assert.Equal([]int{}, DropRight([]int{}, 0))
assert.Equal([]int{}, DropRight([]int{}, 1))
assert.Equal([]int{}, DropRight([]int{}, -1))
assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, 0))
assert.Equal([]int{1, 2, 3, 4}, DropRight([]int{1, 2, 3, 4, 5}, 1))
assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 5))
assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 6))
assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, -1))
tests := []struct {
slice []int
dropNum int
want []int
}{
{[]int{}, 0, []int{}},
{[]int{}, 1, []int{}},
{[]int{}, -1, []int{}},
{[]int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 0, []int{1, 2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, 1, []int{1, 2, 3, 4}},
{[]int{}, 5, []int{}},
{[]int{}, 6, []int{}},
}
for _, tt := range tests {
assert.Equal(tt.want, DropRight(tt.slice, tt.dropNum))
}
}
func TestDropWhile(t *testing.T) {
@@ -648,22 +845,19 @@ func TestDropWhile(t *testing.T) {
assert := internal.NewAssert(t, "TestDropWhile")
numbers := []int{1, 2, 3, 4, 5}
tests := []struct {
slice []int
fn func(int) bool
want []int
}{
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n != 2 }, []int{2, 3, 4, 5}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return true }, []int{}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 0 }, []int{1, 2, 3, 4, 5}},
}
r1 := DropWhile(numbers, func(n int) bool {
return n != 2
})
assert.Equal([]int{2, 3, 4, 5}, r1)
r2 := DropWhile(numbers, func(n int) bool {
return true
})
assert.Equal([]int{}, r2)
r3 := DropWhile(numbers, func(n int) bool {
return n == 0
})
assert.Equal([]int{1, 2, 3, 4, 5}, r3)
for _, tt := range tests {
assert.Equal(tt.want, DropWhile(tt.slice, tt.fn))
}
}
func TestDropRightWhile(t *testing.T) {
@@ -671,22 +865,19 @@ func TestDropRightWhile(t *testing.T) {
assert := internal.NewAssert(t, "TestDropRightWhile")
numbers := []int{1, 2, 3, 4, 5}
tests := []struct {
slice []int
fn func(int) bool
want []int
}{
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n != 2 }, []int{1, 2}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return true }, []int{}},
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 0 }, []int{1, 2, 3, 4, 5}},
}
r1 := DropRightWhile(numbers, func(n int) bool {
return n != 2
})
assert.Equal([]int{1, 2}, r1)
r2 := DropRightWhile(numbers, func(n int) bool {
return true
})
assert.Equal([]int{}, r2)
r3 := DropRightWhile(numbers, func(n int) bool {
return n == 0
})
assert.Equal([]int{1, 2, 3, 4, 5}, r3)
for _, tt := range tests {
assert.Equal(tt.want, DropRightWhile(tt.slice, tt.fn))
}
}
func TestInsertAt(t *testing.T) {
@@ -694,15 +885,25 @@ func TestInsertAt(t *testing.T) {
assert := internal.NewAssert(t, "TestInsertAt")
strs := []string{"a", "b", "c"}
assert.Equal([]string{"a", "b", "c"}, InsertAt(strs, -1, "1"))
assert.Equal([]string{"a", "b", "c"}, InsertAt(strs, 4, "1"))
assert.Equal([]string{"1", "a", "b", "c"}, InsertAt(strs, 0, "1"))
assert.Equal([]string{"a", "1", "b", "c"}, InsertAt(strs, 1, "1"))
assert.Equal([]string{"a", "b", "1", "c"}, InsertAt(strs, 2, "1"))
assert.Equal([]string{"a", "b", "c", "1"}, InsertAt(strs, 3, "1"))
assert.Equal([]string{"1", "2", "3", "a", "b", "c"}, InsertAt(strs, 0, []string{"1", "2", "3"}))
assert.Equal([]string{"a", "b", "c", "1", "2", "3"}, InsertAt(strs, 3, []string{"1", "2", "3"}))
tests := []struct {
slice []string
insertPos int
insertValue any
want []string
}{
{[]string{"a", "b", "c"}, -1, "1", []string{"a", "b", "c"}},
{[]string{"a", "b", "c"}, 4, "1", []string{"a", "b", "c"}},
{[]string{"a", "b", "c"}, 0, "1", []string{"1", "a", "b", "c"}},
{[]string{"a", "b", "c"}, 1, "1", []string{"a", "1", "b", "c"}},
{[]string{"a", "b", "c"}, 2, "1", []string{"a", "b", "1", "c"}},
{[]string{"a", "b", "c"}, 3, "1", []string{"a", "b", "c", "1"}},
{[]string{"a", "b", "c"}, 0, []string{"1", "2", "3"}, []string{"1", "2", "3", "a", "b", "c"}},
{[]string{"a", "b", "c"}, 3, []string{"1", "2", "3"}, []string{"a", "b", "c", "1", "2", "3"}},
}
for _, tt := range tests {
assert.Equal(tt.want, InsertAt(tt.slice, tt.insertPos, tt.insertValue))
}
}
func TestUpdateAt(t *testing.T) {
@@ -733,7 +934,8 @@ func TestUniqueBy(t *testing.T) {
actual := UniqueBy([]int{1, 2, 3, 4, 5, 6}, func(val int) int {
return val % 4
})
assert.Equal([]int{1, 2, 3, 0}, actual)
assert.Equal([]int{1, 2, 3, 4}, actual)
}
func TestUniqueByField(t *testing.T) {
@@ -763,6 +965,58 @@ func TestUniqueByField(t *testing.T) {
}, uniqueUsers)
}
func TestUniqueByComparator(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUniqueByComparator")
t.Run("equal comparison", func(t *testing.T) {
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool {
return item == other
}
result := UniqueByComparator(nums, comparator)
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7}, result)
})
t.Run("unique struct slice by field", func(t *testing.T) {
type student struct {
Name string
Age int
}
students := []student{
{Name: "a", Age: 11},
{Name: "b", Age: 12},
{Name: "a", Age: 13},
{Name: "c", Age: 14},
}
comparator := func(item, other student) bool { return item.Name == other.Name }
result := UniqueByComparator(students, comparator)
assert.Equal([]student{
{Name: "a", Age: 11},
{Name: "b", Age: 12},
{Name: "c", Age: 14},
}, result)
})
t.Run("case-insensitive string comparison", func(t *testing.T) {
stringSlice := []string{"apple", "banana", "Apple", "cherry", "Banana", "date"}
caseInsensitiveComparator := func(item, other string) bool {
return strings.ToLower(item) == strings.ToLower(other)
}
result := UniqueByComparator(stringSlice, caseInsensitiveComparator)
assert.Equal([]string{"apple", "banana", "cherry", "date"}, result)
})
}
func TestUnion(t *testing.T) {
t.Parallel()
@@ -1114,6 +1368,37 @@ func TestIndexOf(t *testing.T) {
assert.Equal(-1, IndexOf(arr3, "r"))
assert.Equal(2, memoryHashCounter[key3])
assert.Equal(0, memoryHashCounter[minKey])
arr4 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
const numGoroutines = 100
var wg sync.WaitGroup
wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func(i int) {
defer wg.Done()
index := IndexOf(arr4, i%10+1)
assert.Equal(i%10, index)
}(i)
}
wg.Wait()
}
func BenchmarkIndexOfDifferentSizes(b *testing.B) {
sizes := []int{10, 100, 1000, 10000, 100000}
for _, size := range sizes {
arr := make([]int, size)
for i := 0; i < len(arr); i++ {
arr[i] = i
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = IndexOf(arr, size/2) // 查找数组中间的元素
}
})
}
}
func TestLastIndexOf(t *testing.T) {
@@ -1448,3 +1733,196 @@ func TestRightPaddingAndLeftPadding(t *testing.T) {
padded := LeftPadding(RightPadding(nums, 0, 3), 0, 3)
assert.Equal([]int{0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0}, padded)
}
func TestUniqueByConcurrent(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestUniqueByConcurrent")
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
comparator := func(item int, other int) bool { return item == other }
result := UniqueByConcurrent(nums, comparator, 4)
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7}, result)
}
func TestMapConcurrent(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMapConcurrent")
t.Run("empty slice", func(t *testing.T) {
actual := MapConcurrent([]int{}, func(_, n int) int { return n * n }, 4)
assert.Equal([]int{}, actual)
})
t.Run("single thread", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6}
expected := []int{1, 4, 9, 16, 25, 36}
actual := MapConcurrent(nums, func(_, n int) int { return n * n }, 1)
assert.Equal(expected, actual)
})
t.Run("multiple threads", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6}
expected := []int{1, 4, 9, 16, 25, 36}
actual := MapConcurrent(nums, func(_, n int) int { return n * n }, 4)
assert.Equal(expected, actual)
})
}
func TestFilterConcurrent(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFilterConcurrent")
t.Run("empty slice", func(t *testing.T) {
actual := FilterConcurrent([]int{}, func(_, n int) bool { return n != 0 }, 4)
assert.Equal([]int{}, actual)
})
t.Run("single thread", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6}
expected := []int{4, 5, 6}
actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 1)
assert.Equal(expected, actual)
})
t.Run("multiple threads", func(t *testing.T) {
nums := []int{1, 2, 3, 4, 5, 6}
expected := []int{4, 5, 6}
actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 4)
assert.Equal(expected, actual)
})
}
func TestFrequency(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestFrequency")
t.Run("empty slice", func(t *testing.T) {
result := Frequency([]int{})
assert.Equal(map[int]int{}, result)
})
t.Run("int slice", func(t *testing.T) {
nums := []int{1, 2, 2, 3, 3, 3}
expected := map[int]int{1: 1, 2: 2, 3: 3}
result := Frequency(nums)
assert.Equal(expected, result)
})
t.Run("string slice", func(t *testing.T) {
strs := []string{"a", "b", "b", "c", "c", "c"}
expected := map[string]int{"a": 1, "b": 2, "c": 3}
result := Frequency(strs)
assert.Equal(expected, result)
})
t.Run("struct slice", func(t *testing.T) {
type student struct {
Name string
Age int
}
students := []student{
{Name: "a", Age: 11},
{Name: "b", Age: 12},
{Name: "a", Age: 13},
{Name: "c", Age: 14},
}
expected := map[student]int{
{Name: "a", Age: 11}: 1,
{Name: "a", Age: 13}: 1,
{Name: "b", Age: 12}: 1,
{Name: "c", Age: 14}: 1,
}
result := Frequency(students)
assert.Equal(expected, result)
})
}
func TestJoinFunc(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestJoinFunc")
t.Run("basic case", func(t *testing.T) {
result := JoinFunc([]int{1, 2, 3}, ", ", func(i int) int {
return i * 2
})
expected := "2, 4, 6"
assert.Equal(expected, result)
})
t.Run("empty slice", func(t *testing.T) {
result := JoinFunc([]int{}, ", ", func(i int) int {
return i * 2
})
assert.Equal("", result)
})
t.Run("single element slice", func(t *testing.T) {
result := JoinFunc([]int{1}, ", ", func(i int) int {
return i * 2
})
assert.Equal("2", result)
})
t.Run("string slice", func(t *testing.T) {
result := JoinFunc([]string{"a", "b", "c"}, ", ", func(s string) string {
return strings.ToUpper(s)
})
expected := "A, B, C"
assert.Equal(expected, result)
})
}
func TestConcatBy(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestConcatBy")
t.Run("Join strings", func(t *testing.T) {
result := ConcatBy([]string{"Hello", "World"}, ", ", func(a, b string) string {
return a + b
})
expected := "Hello, World"
assert.Equal(expected, result)
})
t.Run("Join Person struct", func(t *testing.T) {
type Person struct {
Name string
Age int
}
people := []Person{
{Name: "Alice", Age: 30},
{Name: "Bob", Age: 25},
{Name: "Charlie", Age: 35},
}
sep := Person{Name: " | ", Age: 0}
personConnector := func(a, b Person) Person {
return Person{Name: a.Name + b.Name, Age: a.Age + b.Age}
}
result := ConcatBy(people, sep, personConnector)
assert.Equal("Alice | Bob | Charlie", result.Name)
assert.Equal(90, result.Age)
})
}

View File

@@ -393,6 +393,26 @@ func (s Stream[T]) Min(less func(a, b T) bool) (T, bool) {
return min, true
}
// IndexOf returns the index of the first occurrence of the specified element in this stream, or -1 if this stream does not contain the element.
func (s Stream[T]) IndexOf(target T, equal func(a, b T) bool) int {
for i, v := range s.source {
if equal(v, target) {
return i
}
}
return -1
}
// LastIndexOf returns the index of the last occurrence of the specified element in this stream, or -1 if this stream does not contain the element.
func (s Stream[T]) LastIndexOf(target T, equal func(a, b T) bool) int {
for i := len(s.source) - 1; i >= 0; i-- {
if equal(s.source[i], target) {
return i
}
}
return -1
}
// ToSlice return the elements in the stream.
// Play: https://go.dev/play/p/jI6_iZZuVFE
func (s Stream[T]) ToSlice() []T {

View File

@@ -384,3 +384,31 @@ func ExampleStream_Count() {
// 3
// 0
}
func ExampleStream_IndexOf() {
s := FromSlice([]int{1, 2, 3, 2})
result1 := s.IndexOf(0, func(a, b int) bool { return a == b })
result2 := s.IndexOf(2, func(a, b int) bool { return a == b })
fmt.Println(result1)
fmt.Println(result2)
// Output:
// -1
// 1
}
func ExampleStream_LastIndexOf() {
s := FromSlice([]int{1, 2, 3, 2})
result1 := s.LastIndexOf(0, func(a, b int) bool { return a == b })
result2 := s.LastIndexOf(2, func(a, b int) bool { return a == b })
fmt.Println(result1)
fmt.Println(result2)
// Output:
// -1
// 3
}

View File

@@ -381,3 +381,22 @@ func TestStream_Min(t *testing.T) {
assert.Equal(1, max)
assert.Equal(true, ok)
}
func TestStream_IndexOf(t *testing.T) {
assert := internal.NewAssert(t, "TestStream_IndexOf")
s := FromSlice([]int{4, 2, 1, 3, 4})
assert.Equal(-1, s.IndexOf(0, func(a, b int) bool { return a == b }))
assert.Equal(0, s.IndexOf(4, func(a, b int) bool { return a == b }))
assert.Equal(3, s.IndexOf(3, func(a, b int) bool { return a == b }))
}
func TestStream_LastIndexOf(t *testing.T) {
assert := internal.NewAssert(t, "TestStream_LastIndexOf")
s := FromSlice([]int{4, 2, 1, 3, 2})
assert.Equal(-1, s.LastIndexOf(0, func(a, b int) bool { return a == b }))
assert.Equal(4, s.LastIndexOf(2, func(a, b int) bool { return a == b }))
}

View File

@@ -5,13 +5,18 @@ package strutil
import (
"errors"
"math/rand"
"regexp"
"strings"
"time"
"unicode"
"unicode/utf8"
"unsafe"
)
// used in `Shuffle` function
var rng = rand.New(rand.NewSource(int64(time.Now().UnixNano())))
// CamelCase coverts string to camelCase string. Non letters and numbers will be ignored.
// Play: https://go.dev/play/p/9eXP3tn2tUy
func CamelCase(s string) string {
@@ -70,7 +75,7 @@ func LowerFirst(s string) string {
return string(r) + s[size:]
}
// PadStart pads string on the left and right side if it's shorter than size.
// Pad 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/NzImQq-VF8q
func Pad(source string, size int, padStr string) string {
@@ -621,6 +626,8 @@ func HammingDistance(a, b string) (int, error) {
// Concat uses the strings.Builder to concatenate the input strings.
// - `length` is the expected length of the concatenated string.
// - if you are unsure about the length of the string to be concatenated, please pass 0 or a negative number.
//
// Play: https://go.dev/play/p/gD52SZHr4Kp
func Concat(length int, str ...string) string {
if len(str) == 0 {
return ""
@@ -638,3 +645,114 @@ func Concat(length int, str ...string) string {
}
return sb.String()
}
// Ellipsis truncates a string to a specified length and appends an ellipsis.
// Play: https://go.dev/play/p/i1vbdQiQVRR
func Ellipsis(str string, length int) string {
str = strings.TrimSpace(str)
if length <= 0 {
return ""
}
runes := []rune(str)
if len(runes) <= length {
return str
}
return string(runes[:length]) + "..."
}
// Shuffle the order of characters of given string.
// Play: https://go.dev/play/p/iStFwBwyGY7
func Shuffle(str string) string {
runes := []rune(str)
for i := len(runes) - 1; i > 0; i-- {
j := rng.Intn(i + 1)
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
// Rotate rotates the string by the specified number of characters.
// Play: https://go.dev/play/p/Kf03iOeT5bd
func Rotate(str string, shift int) string {
if shift == 0 {
return str
}
runes := []rune(str)
length := len(runes)
if length == 0 {
return str
}
shift = shift % length
if shift < 0 {
shift = length + shift
}
var sb strings.Builder
sb.Grow(length)
sb.WriteString(string(runes[length-shift:]))
sb.WriteString(string(runes[:length-shift]))
return sb.String()
}
// TemplateReplace replaces the placeholders in the template string with the corresponding values in the data map.
// The placeholders are enclosed in curly braces, e.g. {key}.
// for example, the template string is "Hello, {name}!", and the data map is {"name": "world"},
// the result will be "Hello, world!".
// Play: https://go.dev/play/p/cXSuFvyZqv9
func TemplateReplace(template string, data map[string]string) string {
re := regexp.MustCompile(`\{(\w+)\}`)
result := re.ReplaceAllStringFunc(template, func(s string) string {
key := strings.Trim(s, "{}")
if val, ok := data[key]; ok {
return val
}
return s
})
result = strings.ReplaceAll(result, "{{", "{")
result = strings.ReplaceAll(result, "}}", "}")
return result
}
// RegexMatchAllGroups Matches all subgroups in a string using a regular expression and returns the result.
// Play: https://go.dev/play/p/JZiu0RXpgN-
func RegexMatchAllGroups(pattern, str string) [][]string {
re := regexp.MustCompile(pattern)
matches := re.FindAllStringSubmatch(str, -1)
return matches
}
// ExtractContent extracts the content between the start and end strings in the source string.
// Play: todo
func ExtractContent(s, start, end string) []string {
result := []string{}
for {
if _, after, ok := strings.Cut(s, start); ok {
if before, _, ok := strings.Cut(after, end); ok {
result = append(result, before)
s = after
} else {
break
}
} else {
break
}
}
return result
}

View File

@@ -694,3 +694,74 @@ func ExampleConcat() {
// Go Language
// An apple a daykeeps the doctor away
}
func ExampleEllipsis() {
result1 := Ellipsis("hello world", 5)
result2 := Ellipsis("你好,世界!", 2)
result3 := Ellipsis("😀😃😄😁😆", 3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// hello...
// 你好...
// 😀😃😄...
}
func ExampleRotate() {
result1 := Rotate("Hello", 0)
result2 := Rotate("Hello", 1)
result3 := Rotate("Hello", 2)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello
// oHell
// loHel
}
func ExampleTemplateReplace() {
template := `Hello, my name is {name}, I'm {age} years old.`
data := map[string]string{
"name": "Bob",
"age": "20",
}
result := TemplateReplace(template, data)
fmt.Println(result)
// Output:
// Hello, my name is Bob, I'm 20 years old.
}
func ExampleRegexMatchAllGroups() {
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
str := "Emails: john.doe@example.com and jane.doe@example.com"
result := RegexMatchAllGroups(pattern, str)
fmt.Println(result[0])
fmt.Println(result[1])
// Output:
// [john.doe@example.com john.doe example com]
// [jane.doe@example.com jane.doe example com]
}
func ExampleExtractContent() {
html := `<span>content1</span>aa<span>content2</span>bb<span>content1</span>`
result := ExtractContent(html, "<span>", "</span>")
fmt.Println(result)
// Output:
// [content1 content2 content1]
}

Some files were not shown because too many files have changed in this diff Show More