mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-23 13:52:26 +08:00
Compare commits
125 Commits
11214986cc
...
v2.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73fb8fefd2 | ||
|
|
9cf535055d | ||
|
|
8be7b3e396 | ||
|
|
dac706d700 | ||
|
|
2097277a7d | ||
|
|
95b516e278 | ||
|
|
ca373b00a7 | ||
|
|
a220220f09 | ||
|
|
aeef0418a4 | ||
|
|
9b7d8d7abf | ||
|
|
4d21e81263 | ||
|
|
ce2397422e | ||
|
|
4b3a62b36a | ||
|
|
e054680d20 | ||
|
|
5381842eec | ||
|
|
6e0498514c | ||
|
|
967e6a3493 | ||
|
|
5b24801e49 | ||
|
|
974ba525a6 | ||
|
|
f0235c40b6 | ||
|
|
712a215ea6 | ||
|
|
7893f828d3 | ||
|
|
53fa210f09 | ||
|
|
de9ee08be4 | ||
|
|
5381450bea | ||
|
|
6853d627f4 | ||
|
|
e461acdb72 | ||
|
|
2a796adf85 | ||
|
|
5e6e8d82a8 | ||
|
|
e9280b8c25 | ||
|
|
bb6f10a1fb | ||
|
|
33b4cffe60 | ||
|
|
2b765b49e0 | ||
|
|
004dbdc32e | ||
|
|
ab50e8120a | ||
|
|
73c97af7d8 | ||
|
|
5e8a065eaa | ||
|
|
aa74400607 | ||
|
|
a6eaaef563 | ||
|
|
1b31014f81 | ||
|
|
036847577d | ||
|
|
d21edd1cde | ||
|
|
c58c50327c | ||
|
|
9bfdc686f8 | ||
|
|
5ca8f6ef6f | ||
|
|
a54d4c79a0 | ||
|
|
f7b54986aa | ||
|
|
92fae4273b | ||
|
|
0d29f5437a | ||
|
|
e95d7c82cd | ||
|
|
e138043289 | ||
|
|
aabfcb7bde | ||
|
|
0b5e884371 | ||
|
|
3d1bd08434 | ||
|
|
a62ad71791 | ||
|
|
c02c4f813b | ||
|
|
235d2f2486 | ||
|
|
e9380a3d9f | ||
|
|
81d13c2f1a | ||
|
|
7290296849 | ||
|
|
8a8460a592 | ||
|
|
7a98c431d3 | ||
|
|
606d887230 | ||
|
|
473f9c9f3e | ||
|
|
9ff3d0e79c | ||
|
|
5db1d07d6d | ||
|
|
6c6d14828a | ||
|
|
0e1593c67b | ||
|
|
6c7f38d8b3 | ||
|
|
069812e0ee | ||
|
|
4a539a23c8 | ||
|
|
0b1dab0399 | ||
|
|
805e2543d0 | ||
|
|
a3d518da76 | ||
|
|
e3e2d8394c | ||
|
|
0eeaa06055 | ||
|
|
a43bc554ee | ||
|
|
aebab7c944 | ||
|
|
665bad4ca3 | ||
|
|
e4901e99e9 | ||
|
|
4277e8eca5 | ||
|
|
fdc93c8cc7 | ||
|
|
860a499f98 | ||
|
|
2e1c2276a5 | ||
|
|
d367397dab | ||
|
|
66fd8cf651 | ||
|
|
a6be1828b9 | ||
|
|
8f5d297572 | ||
|
|
a1a4fdc598 | ||
|
|
1610076d22 | ||
|
|
cacbf97223 | ||
|
|
cd156dba5f | ||
|
|
3a71a8697d | ||
|
|
c88fd3db86 | ||
|
|
27d19d1717 | ||
|
|
da24bae6b4 | ||
|
|
3cd9d6b68c | ||
|
|
874d09f331 | ||
|
|
fdf251ac98 | ||
|
|
7ec2533b7a | ||
|
|
9fd0603f4a | ||
|
|
9f7b416a8d | ||
|
|
bf4b2b5fd6 | ||
|
|
22af59565e | ||
|
|
f9e047f190 | ||
|
|
fa298b740d | ||
|
|
6d4fc981b6 | ||
|
|
4c21fe700c | ||
|
|
b7370e8ef8 | ||
|
|
38920e3be6 | ||
|
|
a630a7cda9 | ||
|
|
66dfd9c4fd | ||
|
|
be62aaac9b | ||
|
|
e0c9ccbce3 | ||
|
|
d2d1e5a055 | ||
|
|
bbc58c7e46 | ||
|
|
ac2ecceaec | ||
|
|
a06bb8ee6a | ||
|
|
27b5702fd3 | ||
|
|
b2c3fa0ab8 | ||
|
|
4afc838937 | ||
|
|
3482f80d1c | ||
|
|
565f2893b9 | ||
|
|
1b1b10d0ee | ||
|
|
c5c3888ffc |
8
.github/workflows/codecov.yml
vendored
8
.github/workflows/codecov.yml
vendored
@@ -3,11 +3,11 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
# - v2
|
- rc
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
# - v2
|
- rc
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -17,8 +17,10 @@ jobs:
|
|||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- uses: actions/setup-go@v2
|
- uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: "1.18"
|
go-version: "1.20"
|
||||||
- name: Run coverage
|
- name: Run coverage
|
||||||
run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic
|
run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic
|
||||||
|
- name: Run govet
|
||||||
|
run: go vet -v ./...
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
run: bash <(curl -s https://codecov.io/bash)
|
run: bash <(curl -s https://codecov.io/bash)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ We are excited that you are interested in contributing to lancet. Before submitt
|
|||||||
|
|
||||||
- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass.
|
- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass.
|
||||||
|
|
||||||
- Make sure PRs are created to `v2` branch instead of `master` branch.
|
- Make sure PRs are created to `rc` branch instead of other branch.
|
||||||
|
|
||||||
- If your PR fixes a bug, please provide a description about the related bug.
|
- If your PR fixes a bug, please provide a description about the related bug.
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代
|
|||||||
|
|
||||||
- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。
|
- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。
|
||||||
|
|
||||||
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
|
- 确保 PR 是提交到 `rc` 分支,而不是其他分支。
|
||||||
|
|
||||||
- 如果是修复 bug,请在 PR 中给出描述信息。
|
- 如果是修复 bug,请在 PR 中给出描述信息。
|
||||||
|
|
||||||
|
|||||||
210
README.md
210
README.md
@@ -4,7 +4,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||

|

|
||||||
[](https://github.com/duke-git/lancet/releases)
|
[](https://github.com/duke-git/lancet/releases)
|
||||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||||
@@ -32,13 +32,13 @@
|
|||||||
|
|
||||||
### Note:
|
### Note:
|
||||||
|
|
||||||
1. <b>For users who use go1.18 and above, it is recommended to install lancet v2.x.x. Cause in v2.x.x all functions was rewriten with generics of go1.18.</b>
|
1. <b>For users who use go1.18 and above, it is recommended to install lancet v2.x.x. Cause in v2.x.x all functions were rewritten with generics of go1.18.</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
|
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.2. </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.3. </b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x
|
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x
|
||||||
@@ -201,10 +201,10 @@ import "github.com/duke-git/lancet/v2/concurrency"
|
|||||||
- **<big>OrDone</big>** : read a channel into another channel, will close until cancel context.
|
- **<big>OrDone</big>** : read a channel into another channel, will close until cancel context.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#OrDone)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#OrDone)]
|
||||||
[[play](https://go.dev/play/p/lm_GoS6aDjo)]
|
[[play](https://go.dev/play/p/lm_GoS6aDjo)]
|
||||||
- **<big>Repeat</big>** : create channel, put values into the channel repeatly until cancel the context.
|
- **<big>Repeat</big>** : create channel, put values into the channel repeatedly until cancel the context.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Repeat)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Repeat)]
|
||||||
[[play](https://go.dev/play/p/k5N_ALVmYjE)]
|
[[play](https://go.dev/play/p/k5N_ALVmYjE)]
|
||||||
- **<big>RepeatFn</big>** : create a channel, excutes fn repeatly, and put the result into the channel, until close context.
|
- **<big>RepeatFn</big>** : create a channel, executes fn repeatedly, and put the result into the channel, until close context.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#RepeatFn)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#RepeatFn)]
|
||||||
[[play](https://go.dev/play/p/4J1zAWttP85)]
|
[[play](https://go.dev/play/p/4J1zAWttP85)]
|
||||||
- **<big>Take</big>** : create a channel whose values are taken from another channel with limit number.
|
- **<big>Take</big>** : create a channel whose values are taken from another channel with limit number.
|
||||||
@@ -247,7 +247,7 @@ import "github.com/duke-git/lancet/v2/condition"
|
|||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#TernaryOperator)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#TernaryOperator)]
|
||||||
[[play](https://go.dev/play/p/ElllPZY0guT)]
|
[[play](https://go.dev/play/p/ElllPZY0guT)]
|
||||||
|
|
||||||
<h3 id="convertor"> 5. Convertor package contains some functions for data convertion. <a href="#index">index</a> </h3>
|
<h3 id="convertor"> 5. Convertor package contains some functions for data conversion. <a href="#index">index</a> </h3>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "github.com/duke-git/lancet/v2/convertor"
|
import "github.com/duke-git/lancet/v2/convertor"
|
||||||
@@ -318,6 +318,14 @@ import "github.com/duke-git/lancet/v2/convertor"
|
|||||||
- **<big>GbkToUtf8</big>** : converts GBK encoding data to utf8 encoding data.
|
- **<big>GbkToUtf8</big>** : converts GBK encoding data to utf8 encoding data.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#GbkToUtf8)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#GbkToUtf8)]
|
||||||
[[play](https://go.dev/play/p/OphmHCN_9u8)]
|
[[play](https://go.dev/play/p/OphmHCN_9u8)]
|
||||||
|
- **<big>ToStdBase64</big>** : converts a value to a string encoded in standard Base64.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToStdBase64)]
|
||||||
|
- **<big>ToUrlBase64</big>** : converts a value to a string encoded in url Base64.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToUrlBase64)]
|
||||||
|
- **<big>ToRawStdBase64</big>** : converts a value to a string encoded in raw standard Base64.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawStdBase64)]
|
||||||
|
- **<big>ToRawUrlBase64</big>** : converts a value to a string encoded in raw url Base64.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawUrlBase64)]
|
||||||
|
|
||||||
<h3 id="cryptor"> 6. Cryptor package is for data encryption and decryption. <a href="#index">index</a></h3>
|
<h3 id="cryptor"> 6. Cryptor package is for data encryption and decryption. <a href="#index">index</a></h3>
|
||||||
|
|
||||||
@@ -596,7 +604,7 @@ import "github.com/duke-git/lancet/v2/datetime"
|
|||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TimestampNano)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TimestampNano)]
|
||||||
[[play](https://go.dev/play/p/A9Oq_COrcCF)]
|
[[play](https://go.dev/play/p/A9Oq_COrcCF)]
|
||||||
|
|
||||||
<h3 id="datastructure"> 8. Datastructure package constains some common data structure. eg. list, linklist, stack, queue, set, tree, graph. <a href="#index">index</a></h3>
|
<h3 id="datastructure"> 8. Datastructure package contains some common data structure. eg. list, linklist, stack, queue, set, tree, graph. <a href="#index">index</a></h3>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import list "github.com/duke-git/lancet/v2/datastructure/list"
|
import list "github.com/duke-git/lancet/v2/datastructure/list"
|
||||||
@@ -608,6 +616,7 @@ import set "github.com/duke-git/lancet/v2/datastructure/set"
|
|||||||
import tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
import tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||||
import heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
import heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
||||||
import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||||
|
import optional "github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Structure list:
|
#### Structure list:
|
||||||
@@ -630,6 +639,9 @@ import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
|||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/heap.md)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/heap.md)]
|
||||||
- **<big>Hashmap</big>** : hash map structure.
|
- **<big>Hashmap</big>** : hash map structure.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/hashmap.md)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/hashmap.md)]
|
||||||
|
- **<big>Optional</big>** : Optional container.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/optional.md)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="fileutil"> 9. Fileutil package implements some basic functions for file operations. <a href="#index">index</a></h3>
|
<h3 id="fileutil"> 9. Fileutil package implements some basic functions for file operations. <a href="#index">index</a></h3>
|
||||||
|
|
||||||
@@ -648,9 +660,12 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
|||||||
- **<big>CreateDir</big>** : create directory in absolute path.
|
- **<big>CreateDir</big>** : create directory in absolute path.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CreateDir)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CreateDir)]
|
||||||
[[play](https://go.dev/play/p/qUuCe1OGQnM)]
|
[[play](https://go.dev/play/p/qUuCe1OGQnM)]
|
||||||
- **<big>CopyFile</big>** :copy src file to dest file.
|
- **<big>CopyFile</big>** : copy src file to dest file.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CopyFile)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CopyFile)]
|
||||||
[[play](https://go.dev/play/p/Jg9AMJMLrJi)]
|
[[play](https://go.dev/play/p/Jg9AMJMLrJi)]
|
||||||
|
- **<big>CopyDir</big>** : copy src directory to dest directory.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CopyDir)]
|
||||||
|
[[play](https://go.dev/play/p/YAyFTA_UuPb)]
|
||||||
- **<big>FileMode</big>** : return file's mode and permission.
|
- **<big>FileMode</big>** : return file's mode and permission.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#FileMode)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#FileMode)]
|
||||||
[[play](https://go.dev/play/p/2l2hI42fA3p)]
|
[[play](https://go.dev/play/p/2l2hI42fA3p)]
|
||||||
@@ -706,7 +721,9 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
|||||||
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
|
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
|
||||||
- **<big>WriteCsvFile</big>** : write content to target csv file.
|
- **<big>WriteCsvFile</big>** : write content to target csv file.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteCsvFile)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteCsvFile)]
|
||||||
[[play](https://go.dev/play/p/dAXm58Q5U1o)]
|
- **<big>WriteMapsToCsv</big>** : write slice of map to csv file.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteMapsToCsv)]
|
||||||
|
[[play](https://go.dev/play/p/umAIomZFV1c)]
|
||||||
- **<big>WriteBytesToFile</big>** : write bytes to target file.
|
- **<big>WriteBytesToFile</big>** : write bytes to target file.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteBytesToFile)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteBytesToFile)]
|
||||||
[[play](https://go.dev/play/p/s7QlDxMj3P8)]
|
[[play](https://go.dev/play/p/s7QlDxMj3P8)]
|
||||||
@@ -715,6 +732,12 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
|||||||
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
|
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
|
||||||
- **<big>ReadFile</big>** : read file or url.
|
- **<big>ReadFile</big>** : read file or url.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ReadFile)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ReadFile)]
|
||||||
|
- **<big>ChunkRead</big>** : reads a block from the file at the specified offset and returns all lines within the block.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ChunkRead)]
|
||||||
|
[[play](https://go.dev/play/p/r0hPmKWhsgf)]
|
||||||
|
- **<big>ParallelChunkRead</big>** : reads the file in parallel and send each chunk of lines to the specified channel.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ParallelChunkRead)]
|
||||||
|
[[play](https://go.dev/play/p/teMXnCsdSEw)]
|
||||||
|
|
||||||
<h3 id="formatter"> 10. Formatter contains some functions for data formatting. <a href="#index">index</a></h3>
|
<h3 id="formatter"> 10. Formatter contains some functions for data formatting. <a href="#index">index</a></h3>
|
||||||
|
|
||||||
@@ -754,10 +777,10 @@ import "github.com/duke-git/lancet/v2/function"
|
|||||||
|
|
||||||
#### Function list:
|
#### Function list:
|
||||||
|
|
||||||
- **<big>After</big>** : return a function that invokes passed funcation once the returned function is called more than n times.
|
- **<big>After</big>** : return a function that invokes passed function once the returned function is called more than n times.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#After)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#After)]
|
||||||
[[play](https://go.dev/play/p/eRD5k2vzUVX)]
|
[[play](https://go.dev/play/p/eRD5k2vzUVX)]
|
||||||
- **<big>Before</big>** : return a function that invokes passed funcation once the returned function is called less than n times
|
- **<big>Before</big>** : return a function that invokes passed function once the returned function is called less than n times
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Before)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Before)]
|
||||||
[[play](https://go.dev/play/p/0HqUDIFZ3IL)]
|
[[play](https://go.dev/play/p/0HqUDIFZ3IL)]
|
||||||
- **<big>CurryFn</big>** : make a curry function.
|
- **<big>CurryFn</big>** : make a curry function.
|
||||||
@@ -778,10 +801,32 @@ import "github.com/duke-git/lancet/v2/function"
|
|||||||
- **<big>Pipeline</big>** : takes a list of functions and returns a function whose param will be passed into the functions one by one.
|
- **<big>Pipeline</big>** : takes a list of functions and returns a function whose param will be passed into the functions one by one.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Pipeline)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Pipeline)]
|
||||||
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
|
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
|
||||||
- **<big>Watcher</big>** : Watcher is used for record code excution time. can start/stop/reset the watch timer. get the elapsed time of function execution.
|
- **<big>AcceptIf</big>** : returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#AcceptIf)]
|
||||||
|
[[play](https://go.dev/play/p/XlXHHtzCf7d)]
|
||||||
|
- **<big>And</big>** : returns a composed predicate that represents the logical AND of a list of predicates.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#And)]
|
||||||
|
[[play](https://go.dev/play/p/dTBHJMQ0zD2)]
|
||||||
|
- **<big>Or</big>** : returns a composed predicate that represents the logical OR of a list of predicates.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Or)]
|
||||||
|
[[play](https://go.dev/play/p/LitCIsDFNDA)]
|
||||||
|
- **<big>Negate</big>** : returns a predicate that represents the logical negation of this predicate.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Negate)]
|
||||||
|
[[play](https://go.dev/play/p/jbI8BtgFnVE)]
|
||||||
|
- **<big>Nor</big>** : returns a composed predicate that represents the logical NOR of a list of predicates.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Nor)]
|
||||||
|
[[play](https://go.dev/play/p/2KdCoBEOq84)]
|
||||||
|
- **<big>Nand</big>** : returns a composed predicate that represents the logical Nand of a list of predicates.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Nand)]
|
||||||
|
[[play](https://go.dev/play/p/Rb-FdNGpgSO)]
|
||||||
|
- **<big>Xnor</big>** : returns a composed predicate that represents the logical XNOR of a list of predicates.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Xnor)]
|
||||||
|
[[play](https://go.dev/play/p/FJxko8SFbqc)]
|
||||||
|
- **<big>Watcher</big>** : Watcher is used for record code execution time. can start/stop/reset the watch timer. get the elapsed time of function execution.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Watcher)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Watcher)]
|
||||||
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
|
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="maputil"> 12. Maputil package includes some functions to manipulate map. <a href="#index">index</a></h3>
|
<h3 id="maputil"> 12. Maputil package includes some functions to manipulate map. <a href="#index">index</a></h3>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -793,7 +838,7 @@ import "github.com/duke-git/lancet/v2/maputil"
|
|||||||
- **<big>MapTo</big>** : quick map any value to struct or any base type.
|
- **<big>MapTo</big>** : quick map any value to struct or any base type.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapTo)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapTo)]
|
||||||
[[play](https://go.dev/play/p/4K7KBEPgS5M)]
|
[[play](https://go.dev/play/p/4K7KBEPgS5M)]
|
||||||
- **<big>ForEach</big>** : executes iteratee funcation for every key and value pair in map.
|
- **<big>ForEach</big>** : executes iteratee function for every key and value pair in map.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ForEach)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ForEach)]
|
||||||
[[play](https://go.dev/play/p/OaThj6iNVXK)]
|
[[play](https://go.dev/play/p/OaThj6iNVXK)]
|
||||||
- **<big>Filter</big>** : iterates over map, return a new map contains all key and value pairs pass the predicate function.
|
- **<big>Filter</big>** : iterates over map, return a new map contains all key and value pairs pass the predicate function.
|
||||||
@@ -811,7 +856,7 @@ import "github.com/duke-git/lancet/v2/maputil"
|
|||||||
- **<big>OmitByKeys</big>** : the opposite of FilterByKeys, extracts all the map elements which keys are not omitted.
|
- **<big>OmitByKeys</big>** : the opposite of FilterByKeys, extracts all the map elements which keys are not omitted.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OmitByKeys)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OmitByKeys)]
|
||||||
[[play](https://go.dev/play/p/jXGrWDBfSRp)]
|
[[play](https://go.dev/play/p/jXGrWDBfSRp)]
|
||||||
- **<big>OmitByValues</big>** : the opposite of FilterByValues. remov all elements whose value are in the give slice.
|
- **<big>OmitByValues</big>** : the opposite of FilterByValues. remove all elements whose value are in the give slice.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OmitByValues)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OmitByValues)]
|
||||||
[[play](https://go.dev/play/p/XB7Y10uw20_U)]
|
[[play](https://go.dev/play/p/XB7Y10uw20_U)]
|
||||||
- **<big>Intersect</big>** : iterates over maps, return a new map of key and value pairs in all given maps.
|
- **<big>Intersect</big>** : iterates over maps, return a new map of key and value pairs in all given maps.
|
||||||
@@ -856,6 +901,17 @@ import "github.com/duke-git/lancet/v2/maputil"
|
|||||||
- **<big>HasKey</big>** : checks if map has key or not.
|
- **<big>HasKey</big>** : checks if map has key or not.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#HasKey)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#HasKey)]
|
||||||
[[play](https://go.dev/play/p/isZZHOsDhFc)]
|
[[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)]
|
||||||
|
- **<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)]
|
||||||
|
- **<big>ToSortedSlicesDefault</big>** : converts a map to two slices sorted by key: one for the keys and another for the values.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesDefault)]
|
||||||
|
[[play](https://go.dev/play/p/43gEM2po-qy)]
|
||||||
|
- **<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>NewConcurrentMap</big>** : creates a ConcurrentMap with specific shard count.
|
- **<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)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewConcurrentMap)]
|
||||||
[[play](https://go.dev/play/p/3PenTPETJT0)]
|
[[play](https://go.dev/play/p/3PenTPETJT0)]
|
||||||
@@ -925,6 +981,18 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
|||||||
- **<big>TruncRound</big>** : round off n decimal places for int64.
|
- **<big>TruncRound</big>** : round off n decimal places for int64.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#TruncRound)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#TruncRound)]
|
||||||
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
||||||
|
- **<big>CeilToFloat</big>** : round float up n decimal places.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#CeilToFloat)]
|
||||||
|
[[play](https://go.dev/play/p/8hOeSADZPCo)]
|
||||||
|
- **<big>CeilToString</big>** : round float up n decimal places, then conver to string.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#CeilToString)]
|
||||||
|
[[play](https://go.dev/play/p/wy5bYEyUKKG)]
|
||||||
|
- **<big>FloorToFloat</big>** : round float down n decimal places.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#FloorToFloat)]
|
||||||
|
[[play](https://go.dev/play/p/vbCBrQHZEED)]
|
||||||
|
- **<big>FloorToString</big>** : round float down n decimal places, then conver to string.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#FloorToString)]
|
||||||
|
[[play](https://go.dev/play/p/Qk9KPd2IdDb)]
|
||||||
- **<big>Range</big>** : Creates a slice of numbers from start with specified count, element step is 1.
|
- **<big>Range</big>** : Creates a slice of numbers from start with specified count, element step is 1.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Range)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Range)]
|
||||||
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
|
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
|
||||||
@@ -961,9 +1029,13 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
|||||||
- **<big>Sum</big>** : return sum of passed numbers.
|
- **<big>Sum</big>** : return sum of passed numbers.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)]
|
||||||
[[play](https://go.dev/play/p/1To2ImAMJA7)]
|
[[play](https://go.dev/play/p/1To2ImAMJA7)]
|
||||||
- **<big>Abs</big>** : returns the absolute value of param nubmer.
|
- **<big>Abs</big>** : returns the absolute value of param number.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)]
|
||||||
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
|
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
|
||||||
|
- **<big>Div</big>** : returns the result of x divided by y.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Div)]
|
||||||
|
[[play](https://go.dev/play/p/WLxDdGXXYat)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="netutil"> 14. Netutil package contains functions to get net information and send http request. <a href="#index">index</a></h3>
|
<h3 id="netutil"> 14. Netutil package contains functions to get net information and send http request. <a href="#index">index</a></h3>
|
||||||
|
|
||||||
@@ -1012,7 +1084,7 @@ import "github.com/duke-git/lancet/v2/netutil"
|
|||||||
- **<big>DecodeResponse</big>** : decode http response into target object.
|
- **<big>DecodeResponse</big>** : decode http response into target object.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#DecodeResponse)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#DecodeResponse)]
|
||||||
[[play](https://go.dev/play/p/jUSgynekH7G)]
|
[[play](https://go.dev/play/p/jUSgynekH7G)]
|
||||||
- **<big>StructToUrlValues</big>** : convert struct to url valuse.
|
- **<big>StructToUrlValues</big>** : convert struct to url values.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#StructToUrlValues)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#StructToUrlValues)]
|
||||||
[[play](https://go.dev/play/p/pFqMkM40w9z)]
|
[[play](https://go.dev/play/p/pFqMkM40w9z)]
|
||||||
- **<big>HttpGet<sup>deprecated</sup></big>** : send http get request.
|
- **<big>HttpGet<sup>deprecated</sup></big>** : send http get request.
|
||||||
@@ -1055,7 +1127,7 @@ import "github.com/duke-git/lancet/v2/pointer"
|
|||||||
- **<big>Unwrap</big>** : return the value from the pointer.
|
- **<big>Unwrap</big>** : return the value from the pointer.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#Unwrap)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#Unwrap)]
|
||||||
[[play](https://go.dev/play/p/cgeu3g7cjWb)]
|
[[play](https://go.dev/play/p/cgeu3g7cjWb)]
|
||||||
- **<big>UnwarpOr</big>** : UnwarpOr returns the value from the pointer or fallback if the pointer is nil.
|
- **<big>UnwrapOr</big>** : UnwrapOr returns the value from the pointer or fallback if the pointer is nil.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#UnwrapOr)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#UnwrapOr)]
|
||||||
[[play](https://go.dev/play/p/mmNaLC38W8C)]
|
[[play](https://go.dev/play/p/mmNaLC38W8C)]
|
||||||
- **<big>UnwarpOrDefault</big>** : UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
|
- **<big>UnwarpOrDefault</big>** : UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
|
||||||
@@ -1133,6 +1205,16 @@ import "github.com/duke-git/lancet/v2/retry"
|
|||||||
- **<big>RetryTimes</big>** : set times of retry.
|
- **<big>RetryTimes</big>** : set times of retry.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryTimes)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryTimes)]
|
||||||
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
|
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
|
||||||
|
- **<big>BackoffStrategy</big>** : An interface that defines a method for calculating backoff intervals.
|
||||||
|
[[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)]
|
||||||
|
- **<big>RetryWithLinearBackoff</big>** : set linear strategy backoff.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithLinearBackoff)]
|
||||||
|
- **<big>RetryWithExponentialWithJitterBackoff</big>** : set exponential strategy backoff.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. <a href="#index">index</a></h3>
|
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. <a href="#index">index</a></h3>
|
||||||
|
|
||||||
@@ -1157,7 +1239,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
- **<big>Chunk</big>** : creates a slice of elements split into groups the length of size.
|
- **<big>Chunk</big>** : creates a slice of elements split into groups the length of size.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Chunk)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Chunk)]
|
||||||
[[play](https://go.dev/play/p/b4Pou5j2L_C)]
|
[[play](https://go.dev/play/p/b4Pou5j2L_C)]
|
||||||
- **<big>Compact</big>** : creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey.
|
- **<big>Compact</big>** : creates an slice with all falsy values removed. The values false, nil, 0, and "" are falsy.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Compact)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Compact)]
|
||||||
[[play](https://go.dev/play/p/pO5AnxEr3TK)]
|
[[play](https://go.dev/play/p/pO5AnxEr3TK)]
|
||||||
- **<big>Concat</big>** : creates a new slice concatenating slice with any additional slices.
|
- **<big>Concat</big>** : creates a new slice concatenating slice with any additional slices.
|
||||||
@@ -1178,9 +1260,12 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
- **<big>DifferenceWith</big>** : accepts comparator which is invoked to compare elements of slice to values.
|
- **<big>DifferenceWith</big>** : accepts comparator which is invoked to compare elements of slice to values.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DifferenceWith)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DifferenceWith)]
|
||||||
[[play](https://go.dev/play/p/v2U2deugKuV)]
|
[[play](https://go.dev/play/p/v2U2deugKuV)]
|
||||||
- **<big>DeleteAt</big>** : delete the element of slice from specific start index to end index - 1.
|
- **<big>DeleteAt</big>** : delete the element of slice at index.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteAt)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteAt)]
|
||||||
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
|
[[play](https://go.dev/play/p/800B1dPBYyd)]
|
||||||
|
- **<big>DeleteRange</big>** : delete the element of slice from start index to end index(exclude).
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteRange)]
|
||||||
|
[[play](https://go.dev/play/p/945HwiNrnle)]
|
||||||
- **<big>Drop</big>** : drop n elements from the start of a slice.
|
- **<big>Drop</big>** : drop n elements from the start of a slice.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Drop)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Drop)]
|
||||||
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
|
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
|
||||||
@@ -1238,7 +1323,7 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
- **<big>GroupBy</big>** : iterate over elements of the slice, each element will be group by criteria, returns two slices.
|
- **<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)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#GroupBy)]
|
||||||
[[play](https://go.dev/play/p/QVkPxzPR0iA)]
|
[[play](https://go.dev/play/p/QVkPxzPR0iA)]
|
||||||
- **<big>GroupWith</big>** : return a map composed of keys generated from the resultults of running each element of slice thru iteratee.
|
- **<big>GroupWith</big>** : return a map composed of keys generated from the results of running each element of slice thru iteratee.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#GroupWith)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#GroupWith)]
|
||||||
[[play](https://go.dev/play/p/ApCvMNTLO8a)]
|
[[play](https://go.dev/play/p/ApCvMNTLO8a)]
|
||||||
- **<big>IntSlice<sup>deprecated</sup></big>** : convert param to int slice.
|
- **<big>IntSlice<sup>deprecated</sup></big>** : convert param to int slice.
|
||||||
@@ -1331,6 +1416,8 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
- **<big>UniqueBy</big>** : call iteratee func with every item of slice, then remove duplicated.
|
- **<big>UniqueBy</big>** : call iteratee func with every item of slice, then remove duplicated.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueBy)]
|
[[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/UR323iZLDpv)]
|
||||||
|
- **<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)]
|
||||||
- **<big>Union</big>** : creates a slice of unique elements, in order, from all given slices.
|
- **<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)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Union)]
|
||||||
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
|
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
|
||||||
@@ -1355,6 +1442,17 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
- **<big>Random</big>** : get a random item of slice, return its index, when slice is empty, return -1.
|
- **<big>Random</big>** : get a random item of slice, return its index, when slice is empty, return -1.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Random)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Random)]
|
||||||
[[play](https://go.dev/play/p/UzpGQptWppw)]
|
[[play](https://go.dev/play/p/UzpGQptWppw)]
|
||||||
|
- **<big>SetToDefaultIf</big>** : set elements to their default value if they match the given predicate.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#SetToDefaultIf)]
|
||||||
|
[[play](https://go.dev/play/p/9AXGlPRC0-A)]
|
||||||
|
- **<big>Break</big>** : breaks a list into two parts at the point where the predicate for the first time is true.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Break)]
|
||||||
|
- **<big>RightPadding</big>** : adds padding to the right end of a slice.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#RightPadding)]
|
||||||
|
[[play](https://go.dev/play/p/0_2rlLEMBXL)]
|
||||||
|
- **<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)]
|
||||||
|
|
||||||
|
|
||||||
<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. <a href="#index">index</a></h3>
|
<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. <a href="#index">index</a></h3>
|
||||||
@@ -1377,7 +1475,7 @@ import "github.com/duke-git/lancet/v2/stream"
|
|||||||
- **<big>FromRange</big>** : creates a number stream from start to end. both start and end are included. [start, end]
|
- **<big>FromRange</big>** : creates a number stream from start to end. both start and end are included. [start, end]
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FromRange)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FromRange)]
|
||||||
[[play](https://go.dev/play/p/9Ex1-zcg-B-)]
|
[[play](https://go.dev/play/p/9Ex1-zcg-B-)]
|
||||||
- **<big>Generate</big>** : creates a stream where each element is generated by the provided generater function.
|
- **<big>Generate</big>** : creates a stream where each element is generated by the provided generator function.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Generate)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Generate)]
|
||||||
[[play](https://go.dev/play/p/rkOWL1yA3j9)]
|
[[play](https://go.dev/play/p/rkOWL1yA3j9)]
|
||||||
- **<big>Concat</big>** : creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream.
|
- **<big>Concat</big>** : creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream.
|
||||||
@@ -1422,10 +1520,10 @@ import "github.com/duke-git/lancet/v2/stream"
|
|||||||
- **<big>FindLast</big>** : returns the last element of this stream and true, or zero value and false if the stream is empty.
|
- **<big>FindLast</big>** : returns the last element of this stream and true, or zero value and false if the stream is empty.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FindLast)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FindLast)]
|
||||||
[[play](https://go.dev/play/p/WZD2rDAW-2h)]
|
[[play](https://go.dev/play/p/WZD2rDAW-2h)]
|
||||||
- **<big>Max</big>** : returns the maximum element of this stream according to the provided less function. less fuction: a > b
|
- **<big>Max</big>** : returns the maximum element of this stream according to the provided less function. less function: a > b
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Max)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Max)]
|
||||||
[[play](https://go.dev/play/p/fm-1KOPtGzn)]
|
[[play](https://go.dev/play/p/fm-1KOPtGzn)]
|
||||||
- **<big>Min</big>** : returns the minimum element of this stream according to the provided less function. less fuction: a < b
|
- **<big>Min</big>** : returns the minimum element of this stream according to the provided less function. less function: a < b
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Min)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Min)]
|
||||||
[[play](https://go.dev/play/p/vZfIDgGNRe_0)]
|
[[play](https://go.dev/play/p/vZfIDgGNRe_0)]
|
||||||
- **<big>AllMatch</big>** : returns whether all elements of this stream match the provided predicate.
|
- **<big>AllMatch</big>** : returns whether all elements of this stream match the provided predicate.
|
||||||
@@ -1460,23 +1558,23 @@ import "github.com/duke-git/lancet/v2/structs"
|
|||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Fields)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Fields)]
|
||||||
- **<big>IsStruct</big>** : check if the struct is valid.
|
- **<big>IsStruct</big>** : check if the struct is valid.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsStruct)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsStruct)]
|
||||||
- **<big>Tag</big>** : get a `Tag` of the `Field`, `Tag` is a abstract struct field tag
|
- **<big>Tag</big>** : get a `Tag` of the `Field`, `Tag` is a abstract struct field tag.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Tag)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Tag)]
|
||||||
- **<big>Name</big>** : get the field name.
|
- **<big>Name</big>** : get the field name.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Name)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Name)]
|
||||||
- **<big>Value</big>** : get the `Field` underlying value.
|
- **<big>Value</big>** : get the `Field` underlying value.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Value)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Value)]
|
||||||
- **<big>Kind</big>** : get the field's kind
|
- **<big>Kind</big>** : get the field's kind.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Kind)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Kind)]
|
||||||
- **<big>IsEmbedded</big>** : check if the field is an embedded field.
|
- **<big>IsEmbedded</big>** : check if the field is an embedded field.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsEmbedded)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsEmbedded)]
|
||||||
- **<big>IsExported</big>** : check if the field is exporte
|
- **<big>IsExported</big>** : check if the field is exported.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsExported)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsExported)]
|
||||||
- **<big>IsZero</big>** : check if the field is zero value
|
- **<big>IsZero</big>** : check if the field is zero value.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsZero)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsZero)]
|
||||||
- **<big>IsSlice</big>** : check if the field is a slice
|
- **<big>IsSlice</big>** : check if the field is a slice.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsSlice)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsSlice)]
|
||||||
- **<big>IsTargetType</big>** : check if the field is target type
|
- **<big>IsTargetType</big>** : check if the field is target type.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsTargetType)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsTargetType)]
|
||||||
|
|
||||||
<h3 id="strutil"> 21. Strutil package contains some functions to manipulate string. <a href="#index">index</a></h3>
|
<h3 id="strutil"> 21. Strutil package contains some functions to manipulate string. <a href="#index">index</a></h3>
|
||||||
@@ -1600,6 +1698,12 @@ import "github.com/duke-git/lancet/v2/strutil"
|
|||||||
- **<big>RemoveWhiteSpace</big>** : remove whitespace characters from a string.
|
- **<big>RemoveWhiteSpace</big>** : remove whitespace characters from a string.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#RemoveWhiteSpace)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#RemoveWhiteSpace)]
|
||||||
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
|
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
|
||||||
|
- **<big>SubInBetween</big>** : return substring between the start and end position(excluded) of source string.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#SubInBetween)]
|
||||||
|
[[play](https://go.dev/play/p/EDbaRvjeNsv)]
|
||||||
|
- **<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)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="system"> 22. System package contain some functions about os, runtime, shell command. <a href="#index">index</a></h3>
|
<h3 id="system"> 22. System package contain some functions about os, runtime, shell command. <a href="#index">index</a></h3>
|
||||||
@@ -1646,7 +1750,7 @@ import "github.com/duke-git/lancet/v2/tuple"
|
|||||||
|
|
||||||
#### Function list:
|
#### Function list:
|
||||||
|
|
||||||
- **<big>Tuple2</big>** : represents a 2 elemnets tuple.
|
- **<big>Tuple2</big>** : represents a 2 elements tuple.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple2)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple2)]
|
||||||
[[play](https://go.dev/play/p/3sHVqBQpLYN)]
|
[[play](https://go.dev/play/p/3sHVqBQpLYN)]
|
||||||
- **<big>Tuple2_Unbox</big>** : returns values in Tuple2.
|
- **<big>Tuple2_Unbox</big>** : returns values in Tuple2.
|
||||||
@@ -1658,7 +1762,7 @@ import "github.com/duke-git/lancet/v2/tuple"
|
|||||||
- **<big>Unzip2</big>** : create a group of slice from a slice of Tuple2.
|
- **<big>Unzip2</big>** : create a group of slice from a slice of Tuple2.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip2)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip2)]
|
||||||
[[play](https://go.dev/play/p/KBecr60feXb)]
|
[[play](https://go.dev/play/p/KBecr60feXb)]
|
||||||
- **<big>Tuple3</big>** : represents a 3 elemnets tuple.
|
- **<big>Tuple3</big>** : represents a 3 elements tuple.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple3)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple3)]
|
||||||
[[play](https://go.dev/play/p/FtH2sdCLlCf)]
|
[[play](https://go.dev/play/p/FtH2sdCLlCf)]
|
||||||
- **<big>Tuple3_Unbox</big>** : returns values in Tuple3.
|
- **<big>Tuple3_Unbox</big>** : returns values in Tuple3.
|
||||||
@@ -1670,7 +1774,7 @@ import "github.com/duke-git/lancet/v2/tuple"
|
|||||||
- **<big>Unzip3</big>** : create a group of slice from a slice of Tuple3.
|
- **<big>Unzip3</big>** : create a group of slice from a slice of Tuple3.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip3)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip3)]
|
||||||
[[play](https://go.dev/play/p/bba4cpAa7KO)]
|
[[play](https://go.dev/play/p/bba4cpAa7KO)]
|
||||||
- **<big>Tuple4</big>** : represents a 4 elemnets tuple.
|
- **<big>Tuple4</big>** : represents a 4 elements tuple.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple4)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple4)]
|
||||||
[[play](https://go.dev/play/p/D2EqDz096tk)]
|
[[play](https://go.dev/play/p/D2EqDz096tk)]
|
||||||
- **<big>Tuple4_Unbox</big>** : returns values in Tuple4.
|
- **<big>Tuple4_Unbox</big>** : returns values in Tuple4.
|
||||||
@@ -1682,7 +1786,7 @@ import "github.com/duke-git/lancet/v2/tuple"
|
|||||||
- **<big>Unzip4</big>** : create a group of slice from a slice of Tuple4.
|
- **<big>Unzip4</big>** : create a group of slice from a slice of Tuple4.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip4)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip4)]
|
||||||
[[play](https://go.dev/play/p/rb8z4gyYSRN)]
|
[[play](https://go.dev/play/p/rb8z4gyYSRN)]
|
||||||
- **<big>Tuple5</big>** : represents a 5 elemnets tuple.
|
- **<big>Tuple5</big>** : represents a 5 elements tuple.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple5)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple5)]
|
||||||
[[play](https://go.dev/play/p/2WndmVxPg-r)]
|
[[play](https://go.dev/play/p/2WndmVxPg-r)]
|
||||||
- **<big>Tuple5_Unbox</big>** : returns values in Tuple4.
|
- **<big>Tuple5_Unbox</big>** : returns values in Tuple4.
|
||||||
@@ -1694,7 +1798,7 @@ import "github.com/duke-git/lancet/v2/tuple"
|
|||||||
- **<big>Unzip5</big>** : create a group of slice from a slice of Tuple5.
|
- **<big>Unzip5</big>** : create a group of slice from a slice of Tuple5.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip5)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip5)]
|
||||||
[[play](https://go.dev/play/p/gyl6vKfhqPb)]
|
[[play](https://go.dev/play/p/gyl6vKfhqPb)]
|
||||||
- **<big>Tuple6</big>** : represents a 6 elemnets tuple.
|
- **<big>Tuple6</big>** : represents a 6 elements tuple.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple6)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple6)]
|
||||||
[[play](https://go.dev/play/p/VjqcCwEJZbs)]
|
[[play](https://go.dev/play/p/VjqcCwEJZbs)]
|
||||||
- **<big>Tuple6_Unbox</big>** : returns values in Tuple6.
|
- **<big>Tuple6_Unbox</big>** : returns values in Tuple6.
|
||||||
@@ -1706,7 +1810,7 @@ import "github.com/duke-git/lancet/v2/tuple"
|
|||||||
- **<big>Unzip6</big>** : create a group of slice from a slice of Tuple6.
|
- **<big>Unzip6</big>** : create a group of slice from a slice of Tuple6.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip6)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip6)]
|
||||||
[[play](https://go.dev/play/p/l41XFqCyh5E)]
|
[[play](https://go.dev/play/p/l41XFqCyh5E)]
|
||||||
- **<big>Tuple7</big>** : represents a 7 elemnets tuple.
|
- **<big>Tuple7</big>** : represents a 7 elements tuple.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple7)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple7)]
|
||||||
[[play](https://go.dev/play/p/dzAgv_Ezub9)]
|
[[play](https://go.dev/play/p/dzAgv_Ezub9)]
|
||||||
- **<big>Tuple7_Unbox</big>** : returns values in Tuple7.
|
- **<big>Tuple7_Unbox</big>** : returns values in Tuple7.
|
||||||
@@ -1718,7 +1822,7 @@ import "github.com/duke-git/lancet/v2/tuple"
|
|||||||
- **<big>Unzip7</big>** : create a group of slice from a slice of Tuple7.
|
- **<big>Unzip7</big>** : create a group of slice from a slice of Tuple7.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip7)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip7)]
|
||||||
[[play](https://go.dev/play/p/hws_P1Fr2j3)]
|
[[play](https://go.dev/play/p/hws_P1Fr2j3)]
|
||||||
- **<big>Tuple8</big>** : represents a 8 elemnets tuple.
|
- **<big>Tuple8</big>** : represents a 8 elements tuple.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple8)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple8)]
|
||||||
[[play](https://go.dev/play/p/YA9S0rz3dRz)]
|
[[play](https://go.dev/play/p/YA9S0rz3dRz)]
|
||||||
- **<big>Tuple8_Unbox</big>** : returns values in Tuple8.
|
- **<big>Tuple8_Unbox</big>** : returns values in Tuple8.
|
||||||
@@ -1730,7 +1834,7 @@ import "github.com/duke-git/lancet/v2/tuple"
|
|||||||
- **<big>Unzip8</big>** : create a group of slice from a slice of Tuple8.
|
- **<big>Unzip8</big>** : create a group of slice from a slice of Tuple8.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip8)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip8)]
|
||||||
[[play](https://go.dev/play/p/1SndOwGsZB4)]
|
[[play](https://go.dev/play/p/1SndOwGsZB4)]
|
||||||
- **<big>Tuple9</big>** : represents a 9 elemnets tuple.
|
- **<big>Tuple9</big>** : represents a 9 elements tuple.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple9)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple9)]
|
||||||
[[play](https://go.dev/play/p/yS2NGGtZpQr)]
|
[[play](https://go.dev/play/p/yS2NGGtZpQr)]
|
||||||
- **<big>Tuple9_Unbox</big>** : returns values in Tuple9.
|
- **<big>Tuple9_Unbox</big>** : returns values in Tuple9.
|
||||||
@@ -1742,7 +1846,7 @@ import "github.com/duke-git/lancet/v2/tuple"
|
|||||||
- **<big>Unzip9</big>** : create a group of slice from a slice of Tuple9.
|
- **<big>Unzip9</big>** : create a group of slice from a slice of Tuple9.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip9)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip9)]
|
||||||
[[play](https://go.dev/play/p/91-BU_KURSA)]
|
[[play](https://go.dev/play/p/91-BU_KURSA)]
|
||||||
- **<big>Tuple10</big>** : represents a 10 elemnets tuple.
|
- **<big>Tuple10</big>** : represents a 10 elements tuple.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple10)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple10)]
|
||||||
[[play](https://go.dev/play/p/799qqZg0hUv)]
|
[[play](https://go.dev/play/p/799qqZg0hUv)]
|
||||||
- **<big>Tuple10_Unbox</big>** : returns values in Tuple10.
|
- **<big>Tuple10_Unbox</big>** : returns values in Tuple10.
|
||||||
@@ -1874,19 +1978,19 @@ import "github.com/duke-git/lancet/v2/validator"
|
|||||||
- **<big>IsJWT</big>** : check if a give string is a valid JSON Web Token (JWT).
|
- **<big>IsJWT</big>** : check if a give string is a valid 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/en/api/packages/validator.md#IsJWT)]
|
||||||
[[play](https://go.dev/play/p/R6Op7heJbKI)]
|
[[play](https://go.dev/play/p/R6Op7heJbKI)]
|
||||||
- **<big>IsVisa</big>** : check if a give string is a valid visa card nubmer or not.
|
- **<big>IsVisa</big>** : check if a give string is a valid visa card number or not.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsVisa)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsVisa)]
|
||||||
[[play](https://go.dev/play/p/SdS2keOyJsl)]
|
[[play](https://go.dev/play/p/SdS2keOyJsl)]
|
||||||
- **<big>IsMasterCard</big>** : check if a give string is a valid master card nubmer or not.
|
- **<big>IsMasterCard</big>** : check if a give string is a valid master card number or not.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsMasterCard)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsMasterCard)]
|
||||||
[[play](https://go.dev/play/p/CwWBFRrG27b)]
|
[[play](https://go.dev/play/p/CwWBFRrG27b)]
|
||||||
- **<big>IsAmericanExpress</big>** : check if a give string is a valid american expression card nubmer or not.
|
- **<big>IsAmericanExpress</big>** : check if a give string is a valid american expression card number or not.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsAmericanExpress)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsAmericanExpress)]
|
||||||
[[play](https://go.dev/play/p/HIDFpcOdpkd)]
|
[[play](https://go.dev/play/p/HIDFpcOdpkd)]
|
||||||
- **<big>IsUnionPay</big>** : check if a give string is a valid union pay nubmer or not.
|
- **<big>IsUnionPay</big>** : check if a give string is a valid union pay number or not.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsUnionPay)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsUnionPay)]
|
||||||
[[play](https://go.dev/play/p/CUHPEwEITDf)]
|
[[play](https://go.dev/play/p/CUHPEwEITDf)]
|
||||||
- **<big>IsChinaUnionPay</big>** : check if a give string is a valid china union pay nubmer or not.
|
- **<big>IsChinaUnionPay</big>** : check if a give string is a valid china union pay number or not.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsChinaUnionPay)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsChinaUnionPay)]
|
||||||
[[play](https://go.dev/play/p/yafpdxLiymu)]
|
[[play](https://go.dev/play/p/yafpdxLiymu)]
|
||||||
|
|
||||||
@@ -1940,23 +2044,7 @@ import "github.com/duke-git/lancet/v2/xerror"
|
|||||||
|
|
||||||
## How to Contribute
|
## How to Contribute
|
||||||
|
|
||||||
#### [Contributing Guide](./CONTRIBUTING.en-US.md)
|
#### [Contributing Guide](./CONTRIBUTING.md)
|
||||||
|
|
||||||
|
|
||||||
## Sponsor
|
|
||||||
|
|
||||||
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. 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.
|
|
||||||
|
|
||||||
<div style="position: relative;display: inline-block;">
|
|
||||||
<img src="./docs/public/wechat_pay.png" width="260" height="260"/>
|
|
||||||
<a href="https://en.liberapay.com/Duke_Du/donate" target="\_blank"><img src="./docs/public/sponsor_btn.png" width="220" height="60"/></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
*Donated funds will be used to maintain [lancet](https://www.golancet.cn/en/) website and pay for cloud server costs. Or just buy me a cup of ☕️ when I'm sleepy writing code.*
|
|
||||||
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
Thank you to all the people who contributed to lancet!
|
Thank you to all the people who contributed to lancet!
|
||||||
|
|||||||
137
README_zh-CN.md
137
README_zh-CN.md
@@ -4,7 +4,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||

|

|
||||||
[](https://github.com/duke-git/lancet/releases)
|
[](https://github.com/duke-git/lancet/releases)
|
||||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
|
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
|
||||||
```
|
```
|
||||||
|
|
||||||
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.2。</b>
|
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.3。</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
go get github.com/duke-git/lancet// 使用go1.18以下版本, 必须安装v1.x.x版本
|
go get github.com/duke-git/lancet// 使用go1.18以下版本, 必须安装v1.x.x版本
|
||||||
@@ -100,6 +100,7 @@ func main() {
|
|||||||
- [Validator](#user-content-validator)
|
- [Validator](#user-content-validator)
|
||||||
- [Xerror](#user-content-xerror)
|
- [Xerror](#user-content-xerror)
|
||||||
|
|
||||||
|
|
||||||
<h3 id="algorithm"> 1. algorithm 包实现一些基本查找和排序算法。 <a href="#index">回到目录</a></h3>
|
<h3 id="algorithm"> 1. algorithm 包实现一些基本查找和排序算法。 <a href="#index">回到目录</a></h3>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -317,6 +318,15 @@ import "github.com/duke-git/lancet/v2/convertor"
|
|||||||
- **<big>GbkToUtf8</big>** : GBK 编码转 utf8 编码。
|
- **<big>GbkToUtf8</big>** : GBK 编码转 utf8 编码。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#GbkToUtf8)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#GbkToUtf8)]
|
||||||
[[play](https://go.dev/play/p/OphmHCN_9u8)]
|
[[play](https://go.dev/play/p/OphmHCN_9u8)]
|
||||||
|
- **<big>ToStdBase64</big>** : 将值转换为StdBase64编码的字符串。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToStdBase64)]
|
||||||
|
- **<big>ToUrlBase64</big>** : 将值转换为url Base64编码的字符串。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToUrlBase64)]
|
||||||
|
- **<big>ToRawStdBase64</big>** : 将值转换为RawStdBase64编码的字符串。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawStdBase64)]
|
||||||
|
- **<big>ToRawUrlBase64</big>** : 将值转换为RawUrlBase64编码的字符串。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawUrlBase64)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="cryptor"> 6. cryptor 加密包支持数据加密和解密,获取 md5,hash 值。支持 base64, md5, hmac, aes, des, rsa。 <a href="#index">回到目录</a></h3>
|
<h3 id="cryptor"> 6. cryptor 加密包支持数据加密和解密,获取 md5,hash 值。支持 base64, md5, hmac, aes, des, rsa。 <a href="#index">回到目录</a></h3>
|
||||||
|
|
||||||
@@ -649,9 +659,12 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
|||||||
- **<big>CreateDir</big>** : 创建嵌套目录,例如/a/, /a/b/。
|
- **<big>CreateDir</big>** : 创建嵌套目录,例如/a/, /a/b/。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CreateDir)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CreateDir)]
|
||||||
[[play](https://go.dev/play/p/qUuCe1OGQnM)]
|
[[play](https://go.dev/play/p/qUuCe1OGQnM)]
|
||||||
- **<big>CopyFile</big>** :拷贝文件,会覆盖原有的文件。
|
- **<big>CopyFile</big>** : 拷贝文件,会覆盖原有的文件。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CopyFile)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CopyFile)]
|
||||||
[[play](https://go.dev/play/p/Jg9AMJMLrJi)]
|
[[play](https://go.dev/play/p/Jg9AMJMLrJi)]
|
||||||
|
- **<big>CopyDir</big>** : 拷贝目录。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CopyDir)]
|
||||||
|
[[play](https://go.dev/play/p/YAyFTA_UuPb)]
|
||||||
- **<big>FileMode</big>** : 获取文件 mode 信息。
|
- **<big>FileMode</big>** : 获取文件 mode 信息。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#FileMode)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#FileMode)]
|
||||||
[[play](https://go.dev/play/p/2l2hI42fA3p)]
|
[[play](https://go.dev/play/p/2l2hI42fA3p)]
|
||||||
@@ -705,9 +718,11 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
|||||||
- **<big>ReadCsvFile</big>** : 读取 csv 文件内容到切片。
|
- **<big>ReadCsvFile</big>** : 读取 csv 文件内容到切片。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadCsvFile)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadCsvFile)]
|
||||||
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
|
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
|
||||||
- **<big>WriteCsvFile</big>** : 向 csv 文件写入内容。
|
- **<big>WriteCsvFile</big>** : 向csv文件写入切片数据。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteCsvFile)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteCsvFile)]
|
||||||
[[play](https://go.dev/play/p/dAXm58Q5U1o)]
|
- **<big>WriteMapsToCsv</big>** : 将map切片写入csv文件中。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteMapsToCsv)]
|
||||||
|
[[play](https://go.dev/play/p/umAIomZFV1c)]
|
||||||
- **<big>WriteBytesToFile</big>** : 将 bytes 写入文件。
|
- **<big>WriteBytesToFile</big>** : 将 bytes 写入文件。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteBytesToFile)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteBytesToFile)]
|
||||||
[[play](https://go.dev/play/p/s7QlDxMj3P8)]
|
[[play](https://go.dev/play/p/s7QlDxMj3P8)]
|
||||||
@@ -716,6 +731,14 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
|||||||
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
|
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
|
||||||
- **<big>ReadFile</big>** : 读取文件或者URL。
|
- **<big>ReadFile</big>** : 读取文件或者URL。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFile)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFile)]
|
||||||
|
- **<big>ChunkRead</big>** : 从文件的指定偏移读取块并返回块内所有行。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ChunkRead)]
|
||||||
|
[[play](https://go.dev/play/p/r0hPmKWhsgf)]
|
||||||
|
- **<big>ParallelChunkRead</big>** : 并行读取文件并将每个块的行发送到指定通道。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ParallelChunkRead)]
|
||||||
|
[[play](https://go.dev/play/p/teMXnCsdSEw)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h3 id="formatter"> 10. formatter 格式化器包含一些数据格式化处理方法。 <a href="#index">回到目录</a></h3>
|
<h3 id="formatter"> 10. formatter 格式化器包含一些数据格式化处理方法。 <a href="#index">回到目录</a></h3>
|
||||||
|
|
||||||
@@ -779,10 +802,33 @@ import "github.com/duke-git/lancet/v2/function"
|
|||||||
- **<big>Pipeline</big>** : 从右至左执行函数列表。
|
- **<big>Pipeline</big>** : 从右至左执行函数列表。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Pipeline)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Pipeline)]
|
||||||
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
|
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
|
||||||
|
- **<big>AcceptIf</big>** : AcceptIf函数会返回另一个函数,该函数的签名与apply函数相同,但同时还会包含一个布尔值来表示成功或失败。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#AcceptIf)]
|
||||||
|
[[play](https://go.dev/play/p/XlXHHtzCf7d)]
|
||||||
|
- **<big>And</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑and操作。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#And)]
|
||||||
|
[[play](https://go.dev/play/p/dTBHJMQ0zD2)]
|
||||||
|
- **<big>Or</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑or操作。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Or)]
|
||||||
|
[[play](https://go.dev/play/p/LitCIsDFNDA)]
|
||||||
|
- **<big>Negate</big>** : 返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Negate)]
|
||||||
|
[[play](https://go.dev/play/p/jbI8BtgFnVE)]
|
||||||
|
- **<big>Nor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非或nor的操作。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nor)]
|
||||||
|
[[play](https://go.dev/play/p/2KdCoBEOq84)]
|
||||||
|
- **<big>Nand</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非与nand的操作。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nand)]
|
||||||
|
[[play](https://go.dev/play/p/Rb-FdNGpgSO)]
|
||||||
|
- **<big>Xnor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑异或xnor的操作。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Xnor)]
|
||||||
|
[[play](https://go.dev/play/p/FJxko8SFbqc)]
|
||||||
- **<big>Watcher</big>** : Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。
|
- **<big>Watcher</big>** : Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Watcher)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Watcher)]
|
||||||
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
|
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h3 id="maputil"> 12. maputil 包括一些操作 map 的函数。 <a href="#index">回到目录</a></h3>
|
<h3 id="maputil"> 12. maputil 包括一些操作 map 的函数。 <a href="#index">回到目录</a></h3>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -856,7 +902,18 @@ import "github.com/duke-git/lancet/v2/maputil"
|
|||||||
[[play](https://go.dev/play/p/N9qgYg_Ho6f)]
|
[[play](https://go.dev/play/p/N9qgYg_Ho6f)]
|
||||||
- **<big>HasKey</big>** : 检查 map 是否包含某个 key。
|
- **<big>HasKey</big>** : 检查 map 是否包含某个 key。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#HasKey)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#HasKey)]
|
||||||
|
- **<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/isZZHOsDhFc)]
|
||||||
|
- **<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)]
|
||||||
|
- **<big>ToSortedSlicesDefault</big>** : 将map的key和value转化成两个根据key的值从小到大排序的切片,value切片中元素的位置与key对应。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesDefault)]
|
||||||
|
[[play](https://go.dev/play/p/43gEM2po-qy)]
|
||||||
|
- **<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>NewConcurrentMap</big>** : ConcurrentMap 协程安全的 map 结构。
|
- **<big>NewConcurrentMap</big>** : ConcurrentMap 协程安全的 map 结构。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewConcurrentMap)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewConcurrentMap)]
|
||||||
[[play](https://go.dev/play/p/3PenTPETJT0)]
|
[[play](https://go.dev/play/p/3PenTPETJT0)]
|
||||||
@@ -926,6 +983,18 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
|||||||
- **<big>TruncRound</big>** : 截短 n 位小数(不进行四舍五入)。
|
- **<big>TruncRound</big>** : 截短 n 位小数(不进行四舍五入)。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#TruncRound)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#TruncRound)]
|
||||||
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
||||||
|
- **<big>CeilToFloat</big>** : 向上舍入(进一法),保留n位小数。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToFloat)]
|
||||||
|
[[play](https://go.dev/play/p/8hOeSADZPCo)]
|
||||||
|
- **<big>CeilToString</big>** : 向上舍入(进一法),保留n位小数,返回字符串。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToString)]
|
||||||
|
[[play](https://go.dev/play/p/wy5bYEyUKKG)]
|
||||||
|
- **<big>FloorToFloat</big>** : 向下舍入(去尾法),保留n位小数。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToFloat)]
|
||||||
|
[[play](https://go.dev/play/p/vbCBrQHZEED)]
|
||||||
|
- **<big>FloorToString</big>** : 向下舍入(去尾法),保留n位小数,返回字符串。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToString)]
|
||||||
|
[[play](https://go.dev/play/p/Qk9KPd2IdDb)]
|
||||||
- **<big>Range</big>** : 根据指定的起始值和数量,创建一个数字切片。
|
- **<big>Range</big>** : 根据指定的起始值和数量,创建一个数字切片。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Range)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Range)]
|
||||||
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
|
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
|
||||||
@@ -965,6 +1034,10 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
|||||||
- **<big>Abs</big>** : 求绝对值。
|
- **<big>Abs</big>** : 求绝对值。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Sum)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Sum)]
|
||||||
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
|
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
|
||||||
|
- **<big>Div</big>** : 除法运算。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Div)]
|
||||||
|
[[play](https://go.dev/play/p/WLxDdGXXYat)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="netutil"> 14. netutil 网络包支持获取 ip 地址,发送 http 请求。 <a href="#index">回到目录</a></h3>
|
<h3 id="netutil"> 14. netutil 网络包支持获取 ip 地址,发送 http 请求。 <a href="#index">回到目录</a></h3>
|
||||||
|
|
||||||
@@ -1132,6 +1205,16 @@ import "github.com/duke-git/lancet/v2/retry"
|
|||||||
- **<big>RetryTimes</big>** : 设置重试次数,默认 5。
|
- **<big>RetryTimes</big>** : 设置重试次数,默认 5。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryTimes)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryTimes)]
|
||||||
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
|
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
|
||||||
|
- **<big>BackoffStrategy</big>** : 定义计算退避间隔的方法的接口。
|
||||||
|
[[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)]
|
||||||
|
- **<big>RetryWithLinearBackoff</big>** : 设置线性策略退避。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithLinearBackoff)]
|
||||||
|
- **<big>RetryWithExponentialWithJitterBackoff</big>** : 设置指数策略退避。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h3 id="slice"> 18. slice 包含操作切片的方法集合。 <a href="#index">回到目录</a></h3>
|
<h3 id="slice"> 18. slice 包含操作切片的方法集合。 <a href="#index">回到目录</a></h3>
|
||||||
|
|
||||||
@@ -1177,9 +1260,12 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
- **<big>DifferenceWith</big>** : 接受比较器函数,该比较器被调用以将切片的元素与值进行比较。 结果值的顺序和引用由第一个切片确定。
|
- **<big>DifferenceWith</big>** : 接受比较器函数,该比较器被调用以将切片的元素与值进行比较。 结果值的顺序和引用由第一个切片确定。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DifferenceWith)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DifferenceWith)]
|
||||||
[[play](https://go.dev/play/p/v2U2deugKuV)]
|
[[play](https://go.dev/play/p/v2U2deugKuV)]
|
||||||
- **<big>DeleteAt</big>** : 删除切片中指定开始索引到结束索引的元素。
|
- **<big>DeleteAt</big>** : 删除切片中指定索引到的元素。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteAt)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteAt)]
|
||||||
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
|
[[play](https://go.dev/play/p/800B1dPBYyd)]
|
||||||
|
- **<big>DeleteRange</big>** : 删除切片中指定开始索引到结束索引的元素。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteRange)]
|
||||||
|
[[play](https://go.dev/play/p/945HwiNrnle)]
|
||||||
- **<big>Drop</big>** : 从切片头部删除 n 个元素。
|
- **<big>Drop</big>** : 从切片头部删除 n 个元素。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Drop)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Drop)]
|
||||||
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
|
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
|
||||||
@@ -1330,6 +1416,8 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
- **<big>UniqueBy</big>** : 对切片的每个元素调用 iteratee 函数,然后删除重复元素。
|
- **<big>UniqueBy</big>** : 对切片的每个元素调用 iteratee 函数,然后删除重复元素。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueBy)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueBy)]
|
||||||
[[play](https://go.dev/play/p/UR323iZLDpv)]
|
[[play](https://go.dev/play/p/UR323iZLDpv)]
|
||||||
|
- **<big>UniqueByField</big>** : 根据struct字段对struct切片去重复
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByField)]
|
||||||
- **<big>Union</big>** : 合并多个切片。
|
- **<big>Union</big>** : 合并多个切片。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Union)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Union)]
|
||||||
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
|
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
|
||||||
@@ -1353,6 +1441,17 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
- **<big>Random</big>** : 随机返回切片中元素以及下标, 当切片长度为0时返回下标-1。
|
- **<big>Random</big>** : 随机返回切片中元素以及下标, 当切片长度为0时返回下标-1。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Random)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Random)]
|
||||||
[[play](https://go.dev/play/p/UzpGQptWppw)]
|
[[play](https://go.dev/play/p/UzpGQptWppw)]
|
||||||
|
- **<big>SetToDefaultIf</big>** : 根据给定给定的predicate判定函数来修改切片中的元素。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#SetToDefaultIf)]
|
||||||
|
[[play](https://go.dev/play/p/9AXGlPRC0-A)]
|
||||||
|
- **<big>Break</big>** : 根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Break)]
|
||||||
|
- **<big>RightPadding</big>** : 在切片的右部添加元素。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#RightPadding)]
|
||||||
|
[[play](https://go.dev/play/p/0_2rlLEMBXL)]
|
||||||
|
- **<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)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1443,6 +1542,7 @@ import "github.com/duke-git/lancet/v2/stream"
|
|||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ToSlice)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ToSlice)]
|
||||||
[[play](https://go.dev/play/p/jI6_iZZuVFE)]
|
[[play](https://go.dev/play/p/jI6_iZZuVFE)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="structs"> 20. structs 提供操作 struct, tag, field 的相关函数。 <a href="#index">回到目录</a></h3>
|
<h3 id="structs"> 20. structs 提供操作 struct, tag, field 的相关函数。 <a href="#index">回到目录</a></h3>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -1602,6 +1702,13 @@ import "github.com/duke-git/lancet/v2/strutil"
|
|||||||
- **<big>RemoveWhiteSpace</big>** : 删除字符串中的空格。
|
- **<big>RemoveWhiteSpace</big>** : 删除字符串中的空格。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RemoveWhiteSpace)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RemoveWhiteSpace)]
|
||||||
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
|
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
|
||||||
|
- **<big>SubInBetween</big>** : 获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SubInBetween)]
|
||||||
|
[[play](https://go.dev/play/p/EDbaRvjeNsv)]
|
||||||
|
- **<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)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="system"> 22. system 包含 os, runtime, shell command 的相关函数。 <a href="#index">回到目录</a></h3>
|
<h3 id="system"> 22. system 包含 os, runtime, shell command 的相关函数。 <a href="#index">回到目录</a></h3>
|
||||||
|
|
||||||
@@ -1943,23 +2050,9 @@ import "github.com/duke-git/lancet/v2/xerror"
|
|||||||
|
|
||||||
#### [贡献代码指南](./CONTRIBUTING.zh-CN.md)
|
#### [贡献代码指南](./CONTRIBUTING.zh-CN.md)
|
||||||
|
|
||||||
## 赞助
|
|
||||||
|
|
||||||
您好,我是一名软件开发者,从事开发工作 13 年。热爱软件开源。并愿意为此付出精力。是开源项目 lancet 的作者。Lancet 自两年前开源发布以来,已有超过 1000 个内外部项目使用。lancet 一直会对所有用户免费。您的支持是对我继续奋斗的有力鼓励。谢谢! 微信扫描以下二维码或点击以下赞助按钮发起赞助。
|
|
||||||
|
|
||||||
<div style="position: relative;display: inline-block;">
|
|
||||||
<img src="./docs/public/wechat_pay.png" width="260" height="260"/>
|
|
||||||
<a href="https://liberapay.com/Duke_Du/donate" target="\_blank"><img src="./docs/public/sponsor_btn.png" width="220" height="60"/></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
_捐赠的资金将用于后续[lancet](https://www.golancet.cn/)官网的维护和云服务器的费用支付。或者当我写代码困倦时,给我买杯 ☕️。_
|
|
||||||
|
|
||||||
## 贡献者
|
## 贡献者
|
||||||
|
|
||||||
感谢所有为 lancet 贡献过代码的人!
|
感谢所有为lancet贡献过代码的人!
|
||||||
|
|
||||||
<a href="https://github.com/duke-git/lancet/graphs/contributors">
|
<a href="https://github.com/duke-git/lancet/graphs/contributors">
|
||||||
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
|
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package compare
|
package compare
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ func TestEqual(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
assert := internal.NewAssert(t, "TestEqual")
|
assert := internal.NewAssert(t, "TestEqual")
|
||||||
|
|
||||||
|
// basic type
|
||||||
assert.Equal(true, Equal(1, 1))
|
assert.Equal(true, Equal(1, 1))
|
||||||
assert.Equal(true, Equal(int64(1), int64(1)))
|
assert.Equal(true, Equal(int64(1), int64(1)))
|
||||||
assert.Equal(true, Equal("a", "a"))
|
assert.Equal(true, Equal("a", "a"))
|
||||||
@@ -25,6 +27,7 @@ func TestEqual(t *testing.T) {
|
|||||||
assert.Equal(false, Equal([]int{1, 2}, []int{1, 2, 3}))
|
assert.Equal(false, Equal([]int{1, 2}, []int{1, 2, 3}))
|
||||||
assert.Equal(false, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a"}))
|
assert.Equal(false, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a"}))
|
||||||
|
|
||||||
|
// time
|
||||||
time1 := time.Now()
|
time1 := time.Now()
|
||||||
time2 := time1.Add(time.Second)
|
time2 := time1.Add(time.Second)
|
||||||
time3 := time1.Add(time.Second)
|
time3 := time1.Add(time.Second)
|
||||||
@@ -32,6 +35,7 @@ func TestEqual(t *testing.T) {
|
|||||||
assert.Equal(false, Equal(time1, time2))
|
assert.Equal(false, Equal(time1, time2))
|
||||||
assert.Equal(true, Equal(time2, time3))
|
assert.Equal(true, Equal(time2, time3))
|
||||||
|
|
||||||
|
// struct
|
||||||
st1 := struct {
|
st1 := struct {
|
||||||
A string
|
A string
|
||||||
B string
|
B string
|
||||||
@@ -58,6 +62,19 @@ func TestEqual(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(true, Equal(st1, st2))
|
assert.Equal(true, Equal(st1, st2))
|
||||||
assert.Equal(false, Equal(st1, st3))
|
assert.Equal(false, Equal(st1, st3))
|
||||||
|
|
||||||
|
//byte slice
|
||||||
|
bs1 := []byte("hello")
|
||||||
|
bs2 := []byte("hello")
|
||||||
|
assert.Equal(true, Equal(bs1, bs2))
|
||||||
|
|
||||||
|
// json.Number
|
||||||
|
var jsonNumber1, jsonNumber2 json.Number
|
||||||
|
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||||
|
json.Unmarshal([]byte(`123`), &jsonNumber2)
|
||||||
|
|
||||||
|
assert.Equal(true, Equal(jsonNumber1, jsonNumber2))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEqualValue(t *testing.T) {
|
func TestEqualValue(t *testing.T) {
|
||||||
@@ -69,6 +86,13 @@ func TestEqualValue(t *testing.T) {
|
|||||||
assert.Equal(true, EqualValue(1, "1"))
|
assert.Equal(true, EqualValue(1, "1"))
|
||||||
|
|
||||||
assert.Equal(false, EqualValue(1, "2"))
|
assert.Equal(false, EqualValue(1, "2"))
|
||||||
|
|
||||||
|
// json.Number
|
||||||
|
var jsonNumber1, jsonNumber2 json.Number
|
||||||
|
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||||
|
json.Unmarshal([]byte(`123`), &jsonNumber2)
|
||||||
|
|
||||||
|
assert.Equal(true, EqualValue(jsonNumber1, 123))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLessThan(t *testing.T) {
|
func TestLessThan(t *testing.T) {
|
||||||
@@ -85,6 +109,17 @@ func TestLessThan(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(false, LessThan(1, 1))
|
assert.Equal(false, LessThan(1, 1))
|
||||||
assert.Equal(false, LessThan(1, int64(1)))
|
assert.Equal(false, LessThan(1, int64(1)))
|
||||||
|
|
||||||
|
bs1 := []byte("hello1")
|
||||||
|
bs2 := []byte("hello2")
|
||||||
|
assert.Equal(true, LessThan(bs1, bs2))
|
||||||
|
|
||||||
|
// json.Number
|
||||||
|
var jsonNumber1, jsonNumber2 json.Number
|
||||||
|
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||||
|
json.Unmarshal([]byte(`124`), &jsonNumber2)
|
||||||
|
|
||||||
|
assert.Equal(true, LessThan(jsonNumber1, jsonNumber2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGreaterThan(t *testing.T) {
|
func TestGreaterThan(t *testing.T) {
|
||||||
@@ -102,6 +137,17 @@ func TestGreaterThan(t *testing.T) {
|
|||||||
assert.Equal(false, GreaterThan(1, 2))
|
assert.Equal(false, GreaterThan(1, 2))
|
||||||
assert.Equal(false, GreaterThan(int64(2), 1))
|
assert.Equal(false, GreaterThan(int64(2), 1))
|
||||||
assert.Equal(false, GreaterThan("b", "c"))
|
assert.Equal(false, GreaterThan("b", "c"))
|
||||||
|
|
||||||
|
bs1 := []byte("hello1")
|
||||||
|
bs2 := []byte("hello2")
|
||||||
|
assert.Equal(true, GreaterThan(bs2, bs1))
|
||||||
|
|
||||||
|
// json.Number
|
||||||
|
var jsonNumber1, jsonNumber2 json.Number
|
||||||
|
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||||
|
json.Unmarshal([]byte(`124`), &jsonNumber2)
|
||||||
|
|
||||||
|
assert.Equal(true, GreaterThan(jsonNumber2, jsonNumber1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLessOrEqual(t *testing.T) {
|
func TestLessOrEqual(t *testing.T) {
|
||||||
@@ -119,6 +165,17 @@ func TestLessOrEqual(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(false, LessOrEqual(2, 1))
|
assert.Equal(false, LessOrEqual(2, 1))
|
||||||
assert.Equal(false, LessOrEqual(1, int64(2)))
|
assert.Equal(false, LessOrEqual(1, int64(2)))
|
||||||
|
|
||||||
|
bs1 := []byte("hello1")
|
||||||
|
bs2 := []byte("hello2")
|
||||||
|
assert.Equal(true, LessOrEqual(bs1, bs2))
|
||||||
|
|
||||||
|
// json.Number
|
||||||
|
var jsonNumber1, jsonNumber2 json.Number
|
||||||
|
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||||
|
json.Unmarshal([]byte(`124`), &jsonNumber2)
|
||||||
|
|
||||||
|
assert.Equal(true, LessOrEqual(jsonNumber1, jsonNumber2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGreaterOrEqual(t *testing.T) {
|
func TestGreaterOrEqual(t *testing.T) {
|
||||||
@@ -137,6 +194,17 @@ func TestGreaterOrEqual(t *testing.T) {
|
|||||||
assert.Equal(false, GreaterOrEqual(1, 2))
|
assert.Equal(false, GreaterOrEqual(1, 2))
|
||||||
assert.Equal(false, GreaterOrEqual(int64(2), 1))
|
assert.Equal(false, GreaterOrEqual(int64(2), 1))
|
||||||
assert.Equal(false, GreaterOrEqual("b", "c"))
|
assert.Equal(false, GreaterOrEqual("b", "c"))
|
||||||
|
|
||||||
|
bs1 := []byte("hello1")
|
||||||
|
bs2 := []byte("hello2")
|
||||||
|
assert.Equal(true, GreaterOrEqual(bs2, bs1))
|
||||||
|
|
||||||
|
// json.Number
|
||||||
|
var jsonNumber1, jsonNumber2 json.Number
|
||||||
|
json.Unmarshal([]byte(`123`), &jsonNumber1)
|
||||||
|
json.Unmarshal([]byte(`124`), &jsonNumber2)
|
||||||
|
|
||||||
|
assert.Equal(true, GreaterOrEqual(jsonNumber2, jsonNumber1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInDelta(t *testing.T) {
|
func TestInDelta(t *testing.T) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package convertor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -394,3 +395,91 @@ func GbkToUtf8(bs []byte) ([]byte, error) {
|
|||||||
b, err := io.ReadAll(r)
|
b, err := io.ReadAll(r)
|
||||||
return b, err
|
return b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToStdBase64 convert data to standard base64 encoding.
|
||||||
|
// Play: https://go.dev/play/p/_fLJqJD3NMo
|
||||||
|
func ToStdBase64(value any) string {
|
||||||
|
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
switch value.(type) {
|
||||||
|
case []byte:
|
||||||
|
return base64.StdEncoding.EncodeToString(value.([]byte))
|
||||||
|
case string:
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(value.(string)))
|
||||||
|
case error:
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(value.(error).Error()))
|
||||||
|
default:
|
||||||
|
marshal, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(marshal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUrlBase64 convert data to URL base64 encoding.
|
||||||
|
// Play: https://go.dev/play/p/C_d0GlvEeUR
|
||||||
|
func ToUrlBase64(value any) string {
|
||||||
|
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
switch value.(type) {
|
||||||
|
case []byte:
|
||||||
|
return base64.URLEncoding.EncodeToString(value.([]byte))
|
||||||
|
case string:
|
||||||
|
return base64.URLEncoding.EncodeToString([]byte(value.(string)))
|
||||||
|
case error:
|
||||||
|
return base64.URLEncoding.EncodeToString([]byte(value.(error).Error()))
|
||||||
|
default:
|
||||||
|
marshal, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return base64.URLEncoding.EncodeToString(marshal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToRawStdBase64 convert data to raw standard base64 encoding.
|
||||||
|
// Play: https://go.dev/play/p/wSAr3sfkDcv
|
||||||
|
func ToRawStdBase64(value any) string {
|
||||||
|
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
switch value.(type) {
|
||||||
|
case []byte:
|
||||||
|
return base64.RawStdEncoding.EncodeToString(value.([]byte))
|
||||||
|
case string:
|
||||||
|
return base64.RawStdEncoding.EncodeToString([]byte(value.(string)))
|
||||||
|
case error:
|
||||||
|
return base64.RawStdEncoding.EncodeToString([]byte(value.(error).Error()))
|
||||||
|
default:
|
||||||
|
marshal, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return base64.RawStdEncoding.EncodeToString(marshal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToRawUrlBase64 convert data to raw URL base64 encoding.
|
||||||
|
// Play: https://go.dev/play/p/HwdDPFcza1O
|
||||||
|
func ToRawUrlBase64(value any) string {
|
||||||
|
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
switch value.(type) {
|
||||||
|
case []byte:
|
||||||
|
return base64.RawURLEncoding.EncodeToString(value.([]byte))
|
||||||
|
case string:
|
||||||
|
return base64.RawURLEncoding.EncodeToString([]byte(value.(string)))
|
||||||
|
case error:
|
||||||
|
return base64.RawURLEncoding.EncodeToString([]byte(value.(error).Error()))
|
||||||
|
default:
|
||||||
|
marshal, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return base64.RawURLEncoding.EncodeToString(marshal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package convertor
|
package convertor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -391,3 +392,182 @@ func ExampleGbkToUtf8() {
|
|||||||
// true
|
// true
|
||||||
// hello
|
// hello
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleToStdBase64() {
|
||||||
|
// if you want to see the result, please use 'base64.StdEncoding.DecodeString()' to decode the result
|
||||||
|
|
||||||
|
afterEncode := ToStdBase64(nil)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode = ToStdBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = ToStdBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = ToStdBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = ToStdBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = ToStdBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = ToStdBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = ToStdBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
//
|
||||||
|
// aGVsbG8=
|
||||||
|
// aGVsbG8=
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||||
|
// MTIzLjQ1Ng==
|
||||||
|
// dHJ1ZQ==
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleToUrlBase64() {
|
||||||
|
// if you want to see the result, please use 'base64.URLEncoding.DecodeString()' to decode the result
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode := ToUrlBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = ToUrlBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = ToUrlBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = ToUrlBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = ToUrlBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = ToUrlBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = ToUrlBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// aGVsbG8=
|
||||||
|
// aGVsbG8=
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||||
|
// MTIzLjQ1Ng==
|
||||||
|
// dHJ1ZQ==
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleToRawStdBase64() {
|
||||||
|
// if you want to see the result, please use 'base64.RawStdEncoding.DecodeString()' to decode the result
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode := ToRawStdBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = ToRawStdBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = ToRawStdBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = ToRawStdBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = ToRawStdBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = ToRawStdBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = ToRawStdBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// aGVsbG8
|
||||||
|
// aGVsbG8
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||||
|
// MTIzLjQ1Ng
|
||||||
|
// dHJ1ZQ
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleToRawUrlBase64() {
|
||||||
|
// if you want to see the result, please use 'base64.RawURLEncoding.DecodeString()' to decode the result
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode := ToRawUrlBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = ToRawUrlBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = ToRawUrlBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = ToRawUrlBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = ToRawUrlBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = ToRawUrlBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = ToRawUrlBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// aGVsbG8
|
||||||
|
// aGVsbG8
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||||
|
// MTIzLjQ1Ng
|
||||||
|
// dHJ1ZQ
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package convertor
|
package convertor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
"github.com/duke-git/lancet/v2/slice"
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
@@ -461,3 +465,279 @@ func TestGbkToUtf8(t *testing.T) {
|
|||||||
assert.Equal(true, utf8.Valid(utf8Data))
|
assert.Equal(true, utf8.Valid(utf8Data))
|
||||||
assert.Equal("hello", string(utf8Data))
|
assert.Equal("hello", string(utf8Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestToStdBase64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestToStdBase64")
|
||||||
|
|
||||||
|
r1 := ToStdBase64("abc")
|
||||||
|
d1, _ := base64.StdEncoding.DecodeString(r1)
|
||||||
|
assert.Equal("abc", string(d1))
|
||||||
|
|
||||||
|
r2 := ToStdBase64([]byte("abc"))
|
||||||
|
d2, _ := base64.StdEncoding.DecodeString(r2)
|
||||||
|
assert.Equal("abc", string(d2))
|
||||||
|
|
||||||
|
r3 := ToStdBase64(123)
|
||||||
|
d3, _ := base64.StdEncoding.DecodeString(r3)
|
||||||
|
assert.Equal("123", string(d3))
|
||||||
|
|
||||||
|
r4 := ToStdBase64(11.11)
|
||||||
|
d4, _ := base64.StdEncoding.DecodeString(r4)
|
||||||
|
assert.Equal("11.11", string(d4))
|
||||||
|
|
||||||
|
r5 := ToStdBase64(map[string]any{"name": "duke", "quantity": 1})
|
||||||
|
d5, _ := base64.StdEncoding.DecodeString(r5)
|
||||||
|
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
|
||||||
|
|
||||||
|
r6 := ToStdBase64([]int64{7, 5, 9, 4, 23})
|
||||||
|
d6, _ := base64.StdEncoding.DecodeString(r6)
|
||||||
|
assert.Equal("[7,5,9,4,23]", string(d6))
|
||||||
|
|
||||||
|
r7 := ToStdBase64([]string{"7", "5", "9", "4", "23"})
|
||||||
|
d7, _ := base64.StdEncoding.DecodeString(r7)
|
||||||
|
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
|
||||||
|
|
||||||
|
r8 := ToStdBase64(nil)
|
||||||
|
d8, _ := base64.StdEncoding.DecodeString(r8)
|
||||||
|
assert.Equal("", string(d8))
|
||||||
|
|
||||||
|
ch := make(chan int, 3)
|
||||||
|
ch <- 1
|
||||||
|
ch <- 2
|
||||||
|
r9 := ToStdBase64(ch)
|
||||||
|
d9, _ := base64.StdEncoding.DecodeString(r9)
|
||||||
|
assert.Equal("", string(d9))
|
||||||
|
|
||||||
|
r10 := ToStdBase64(io.EOF)
|
||||||
|
d10, _ := base64.StdEncoding.DecodeString(r10)
|
||||||
|
assert.Equal("EOF", string(d10))
|
||||||
|
|
||||||
|
r11 := ToStdBase64(errors.New("test"))
|
||||||
|
d11, _ := base64.StdEncoding.DecodeString(r11)
|
||||||
|
assert.Equal("test", string(d11))
|
||||||
|
|
||||||
|
typedNil := (*int)(nil)
|
||||||
|
r12 := ToStdBase64(typedNil)
|
||||||
|
d12, _ := base64.StdEncoding.DecodeString(r12)
|
||||||
|
assert.Equal("", string(d12))
|
||||||
|
|
||||||
|
type nilInterface interface {
|
||||||
|
}
|
||||||
|
var nI nilInterface = nil
|
||||||
|
d13, _ := base64.StdEncoding.DecodeString(ToStdBase64(nI))
|
||||||
|
assert.Equal("", string(d13))
|
||||||
|
|
||||||
|
var p unsafe.Pointer
|
||||||
|
d14, _ := base64.StdEncoding.DecodeString(ToStdBase64(p))
|
||||||
|
assert.Equal("", string(d14))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToUrlBase64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestToUrlBase64")
|
||||||
|
|
||||||
|
r1 := ToUrlBase64("abc")
|
||||||
|
d1, _ := base64.URLEncoding.DecodeString(r1)
|
||||||
|
assert.Equal("abc", string(d1))
|
||||||
|
|
||||||
|
r2 := ToUrlBase64([]byte("abc"))
|
||||||
|
d2, _ := base64.URLEncoding.DecodeString(r2)
|
||||||
|
assert.Equal("abc", string(d2))
|
||||||
|
|
||||||
|
r3 := ToUrlBase64(123)
|
||||||
|
d3, _ := base64.URLEncoding.DecodeString(r3)
|
||||||
|
assert.Equal("123", string(d3))
|
||||||
|
|
||||||
|
r4 := ToUrlBase64(11.11)
|
||||||
|
d4, _ := base64.URLEncoding.DecodeString(r4)
|
||||||
|
assert.Equal("11.11", string(d4))
|
||||||
|
|
||||||
|
r5 := ToUrlBase64(map[string]any{"name": "duke", "quantity": 1})
|
||||||
|
d5, _ := base64.URLEncoding.DecodeString(r5)
|
||||||
|
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
|
||||||
|
|
||||||
|
r6 := ToUrlBase64([]int64{7, 5, 9, 4, 23})
|
||||||
|
d6, _ := base64.URLEncoding.DecodeString(r6)
|
||||||
|
assert.Equal("[7,5,9,4,23]", string(d6))
|
||||||
|
|
||||||
|
r7 := ToUrlBase64([]string{"7", "5", "9", "4", "23"})
|
||||||
|
d7, _ := base64.URLEncoding.DecodeString(r7)
|
||||||
|
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
|
||||||
|
|
||||||
|
r8 := ToUrlBase64(nil)
|
||||||
|
d8, _ := base64.URLEncoding.DecodeString(r8)
|
||||||
|
assert.Equal("", string(d8))
|
||||||
|
|
||||||
|
ch := make(chan int, 3)
|
||||||
|
ch <- 1
|
||||||
|
ch <- 2
|
||||||
|
r9 := ToUrlBase64(ch)
|
||||||
|
d9, _ := base64.URLEncoding.DecodeString(r9)
|
||||||
|
assert.Equal("", string(d9))
|
||||||
|
|
||||||
|
r10 := ToUrlBase64(io.EOF)
|
||||||
|
d10, _ := base64.URLEncoding.DecodeString(r10)
|
||||||
|
assert.Equal("EOF", string(d10))
|
||||||
|
|
||||||
|
r11 := ToUrlBase64(errors.New("test"))
|
||||||
|
d11, _ := base64.URLEncoding.DecodeString(r11)
|
||||||
|
assert.Equal("test", string(d11))
|
||||||
|
|
||||||
|
typedNil := (*int)(nil)
|
||||||
|
r12 := ToUrlBase64(typedNil)
|
||||||
|
d12, _ := base64.URLEncoding.DecodeString(r12)
|
||||||
|
assert.Equal("", string(d12))
|
||||||
|
|
||||||
|
type nilInterface interface {
|
||||||
|
}
|
||||||
|
var nI nilInterface = nil
|
||||||
|
d13, _ := base64.URLEncoding.DecodeString(ToUrlBase64(nI))
|
||||||
|
assert.Equal("", string(d13))
|
||||||
|
|
||||||
|
var p unsafe.Pointer
|
||||||
|
d14, _ := base64.URLEncoding.DecodeString(ToUrlBase64(p))
|
||||||
|
assert.Equal("", string(d14))
|
||||||
|
|
||||||
|
r15 := ToUrlBase64("4+3/4?=")
|
||||||
|
d15, _ := base64.URLEncoding.DecodeString(r15)
|
||||||
|
assert.Equal("4+3/4?=", string(d15))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToRawStdBase64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestToRawStdBase64")
|
||||||
|
|
||||||
|
r1 := ToRawStdBase64("abc")
|
||||||
|
d1, _ := base64.RawStdEncoding.DecodeString(r1)
|
||||||
|
assert.Equal("abc", string(d1))
|
||||||
|
|
||||||
|
r2 := ToRawStdBase64([]byte("abc"))
|
||||||
|
d2, _ := base64.RawStdEncoding.DecodeString(r2)
|
||||||
|
assert.Equal("abc", string(d2))
|
||||||
|
|
||||||
|
r3 := ToRawStdBase64(123)
|
||||||
|
d3, _ := base64.RawStdEncoding.DecodeString(r3)
|
||||||
|
assert.Equal("123", string(d3))
|
||||||
|
|
||||||
|
r4 := ToRawStdBase64(11.11)
|
||||||
|
d4, _ := base64.RawStdEncoding.DecodeString(r4)
|
||||||
|
assert.Equal("11.11", string(d4))
|
||||||
|
|
||||||
|
r5 := ToRawStdBase64(map[string]any{"name": "duke", "quantity": 1})
|
||||||
|
d5, _ := base64.RawStdEncoding.DecodeString(r5)
|
||||||
|
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
|
||||||
|
|
||||||
|
r6 := ToRawStdBase64([]int64{7, 5, 9, 4, 23})
|
||||||
|
d6, _ := base64.RawStdEncoding.DecodeString(r6)
|
||||||
|
assert.Equal("[7,5,9,4,23]", string(d6))
|
||||||
|
|
||||||
|
r7 := ToRawStdBase64([]string{"7", "5", "9", "4", "23"})
|
||||||
|
d7, _ := base64.RawStdEncoding.DecodeString(r7)
|
||||||
|
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
|
||||||
|
|
||||||
|
r8 := ToRawStdBase64(nil)
|
||||||
|
d8, _ := base64.RawStdEncoding.DecodeString(r8)
|
||||||
|
assert.Equal("", string(d8))
|
||||||
|
|
||||||
|
ch := make(chan int, 3)
|
||||||
|
ch <- 1
|
||||||
|
ch <- 2
|
||||||
|
r9 := ToRawStdBase64(ch)
|
||||||
|
d9, _ := base64.RawStdEncoding.DecodeString(r9)
|
||||||
|
assert.Equal("", string(d9))
|
||||||
|
|
||||||
|
r10 := ToRawStdBase64(io.EOF)
|
||||||
|
d10, _ := base64.RawStdEncoding.DecodeString(r10)
|
||||||
|
assert.Equal("EOF", string(d10))
|
||||||
|
|
||||||
|
r11 := ToRawStdBase64(errors.New("test"))
|
||||||
|
d11, _ := base64.RawStdEncoding.DecodeString(r11)
|
||||||
|
assert.Equal("test", string(d11))
|
||||||
|
|
||||||
|
typedNil := (*int)(nil)
|
||||||
|
r12 := ToRawStdBase64(typedNil)
|
||||||
|
d12, _ := base64.RawStdEncoding.DecodeString(r12)
|
||||||
|
assert.Equal("", string(d12))
|
||||||
|
|
||||||
|
type nilInterface interface {
|
||||||
|
}
|
||||||
|
var nI nilInterface = nil
|
||||||
|
d13, _ := base64.RawStdEncoding.DecodeString(ToRawStdBase64(nI))
|
||||||
|
assert.Equal("", string(d13))
|
||||||
|
|
||||||
|
var p unsafe.Pointer
|
||||||
|
d14, _ := base64.RawStdEncoding.DecodeString(ToRawStdBase64(p))
|
||||||
|
assert.Equal("", string(d14))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToRawUrlBase64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestToRawUrlBase64")
|
||||||
|
|
||||||
|
r1 := ToRawUrlBase64("abc")
|
||||||
|
d1, _ := base64.RawURLEncoding.DecodeString(r1)
|
||||||
|
assert.Equal("abc", string(d1))
|
||||||
|
|
||||||
|
r2 := ToRawUrlBase64([]byte("abc"))
|
||||||
|
d2, _ := base64.RawURLEncoding.DecodeString(r2)
|
||||||
|
assert.Equal("abc", string(d2))
|
||||||
|
|
||||||
|
r3 := ToRawUrlBase64(123)
|
||||||
|
d3, _ := base64.RawURLEncoding.DecodeString(r3)
|
||||||
|
assert.Equal("123", string(d3))
|
||||||
|
|
||||||
|
r4 := ToRawUrlBase64(11.11)
|
||||||
|
d4, _ := base64.RawURLEncoding.DecodeString(r4)
|
||||||
|
assert.Equal("11.11", string(d4))
|
||||||
|
|
||||||
|
r5 := ToRawUrlBase64(map[string]any{"name": "duke", "quantity": 1})
|
||||||
|
d5, _ := base64.RawURLEncoding.DecodeString(r5)
|
||||||
|
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
|
||||||
|
|
||||||
|
r6 := ToRawUrlBase64([]int64{7, 5, 9, 4, 23})
|
||||||
|
d6, _ := base64.RawURLEncoding.DecodeString(r6)
|
||||||
|
assert.Equal("[7,5,9,4,23]", string(d6))
|
||||||
|
|
||||||
|
r7 := ToRawUrlBase64([]string{"7", "5", "9", "4", "23"})
|
||||||
|
d7, _ := base64.RawURLEncoding.DecodeString(r7)
|
||||||
|
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
|
||||||
|
|
||||||
|
r8 := ToRawUrlBase64(nil)
|
||||||
|
d8, _ := base64.RawURLEncoding.DecodeString(r8)
|
||||||
|
assert.Equal("", string(d8))
|
||||||
|
|
||||||
|
ch := make(chan int, 3)
|
||||||
|
ch <- 1
|
||||||
|
ch <- 2
|
||||||
|
r9 := ToRawUrlBase64(ch)
|
||||||
|
d9, _ := base64.RawURLEncoding.DecodeString(r9)
|
||||||
|
assert.Equal("", string(d9))
|
||||||
|
|
||||||
|
r10 := ToRawUrlBase64(io.EOF)
|
||||||
|
d10, _ := base64.RawURLEncoding.DecodeString(r10)
|
||||||
|
assert.Equal("EOF", string(d10))
|
||||||
|
|
||||||
|
r11 := ToRawUrlBase64(errors.New("test"))
|
||||||
|
d11, _ := base64.RawURLEncoding.DecodeString(r11)
|
||||||
|
assert.Equal("test", string(d11))
|
||||||
|
|
||||||
|
typedNil := (*int)(nil)
|
||||||
|
r12 := ToRawUrlBase64(typedNil)
|
||||||
|
d12, _ := base64.RawURLEncoding.DecodeString(r12)
|
||||||
|
assert.Equal("", string(d12))
|
||||||
|
|
||||||
|
type nilInterface interface {
|
||||||
|
}
|
||||||
|
var nI nilInterface = nil
|
||||||
|
d13, _ := base64.RawURLEncoding.DecodeString(ToRawUrlBase64(nI))
|
||||||
|
assert.Equal("", string(d13))
|
||||||
|
|
||||||
|
var p unsafe.Pointer
|
||||||
|
d14, _ := base64.RawURLEncoding.DecodeString(ToRawUrlBase64(p))
|
||||||
|
assert.Equal("", string(d14))
|
||||||
|
|
||||||
|
r15 := ToRawUrlBase64("4+3/4?=")
|
||||||
|
d15, _ := base64.RawURLEncoding.DecodeString(r15)
|
||||||
|
assert.Equal("4+3/4?=", string(d15))
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ func AesEcbDecrypt(encrypted, key []byte) []byte {
|
|||||||
// len(key) should be 16, 24 or 32.
|
// len(key) should be 16, 24 or 32.
|
||||||
// Play: https://go.dev/play/p/IOq_g8_lKZD
|
// Play: https://go.dev/play/p/IOq_g8_lKZD
|
||||||
func AesCbcEncrypt(data, key []byte) []byte {
|
func AesCbcEncrypt(data, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 16 && size != 24 && size != 32 {
|
||||||
|
panic("key length shoud be 16 or 24 or 32")
|
||||||
|
}
|
||||||
|
|
||||||
block, _ := aes.NewCipher(key)
|
block, _ := aes.NewCipher(key)
|
||||||
data = pkcs7Padding(data, block.BlockSize())
|
data = pkcs7Padding(data, block.BlockSize())
|
||||||
|
|
||||||
@@ -95,6 +100,11 @@ func AesCbcEncrypt(data, key []byte) []byte {
|
|||||||
// len(key) should be 16, 24 or 32.
|
// len(key) should be 16, 24 or 32.
|
||||||
// Play: https://go.dev/play/p/IOq_g8_lKZD
|
// Play: https://go.dev/play/p/IOq_g8_lKZD
|
||||||
func AesCbcDecrypt(encrypted, key []byte) []byte {
|
func AesCbcDecrypt(encrypted, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 16 && size != 24 && size != 32 {
|
||||||
|
panic("key length shoud be 16 or 24 or 32")
|
||||||
|
}
|
||||||
|
|
||||||
block, _ := aes.NewCipher(key)
|
block, _ := aes.NewCipher(key)
|
||||||
|
|
||||||
iv := encrypted[:aes.BlockSize]
|
iv := encrypted[:aes.BlockSize]
|
||||||
@@ -111,6 +121,11 @@ func AesCbcDecrypt(encrypted, key []byte) []byte {
|
|||||||
// len(key) should be 16, 24 or 32.
|
// len(key) should be 16, 24 or 32.
|
||||||
// Play: https://go.dev/play/p/SpaZO0-5Nsp
|
// Play: https://go.dev/play/p/SpaZO0-5Nsp
|
||||||
func AesCtrCrypt(data, key []byte) []byte {
|
func AesCtrCrypt(data, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 16 && size != 24 && size != 32 {
|
||||||
|
panic("key length shoud be 16 or 24 or 32")
|
||||||
|
}
|
||||||
|
|
||||||
block, _ := aes.NewCipher(key)
|
block, _ := aes.NewCipher(key)
|
||||||
|
|
||||||
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
||||||
@@ -126,6 +141,11 @@ func AesCtrCrypt(data, key []byte) []byte {
|
|||||||
// len(key) should be 16, 24 or 32.
|
// len(key) should be 16, 24 or 32.
|
||||||
// Play: https://go.dev/play/p/tfkF10B13kH
|
// Play: https://go.dev/play/p/tfkF10B13kH
|
||||||
func AesCfbEncrypt(data, key []byte) []byte {
|
func AesCfbEncrypt(data, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 16 && size != 24 && size != 32 {
|
||||||
|
panic("key length shoud be 16 or 24 or 32")
|
||||||
|
}
|
||||||
|
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -148,6 +168,11 @@ func AesCfbEncrypt(data, key []byte) []byte {
|
|||||||
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32.
|
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32.
|
||||||
// Play: https://go.dev/play/p/tfkF10B13kH
|
// Play: https://go.dev/play/p/tfkF10B13kH
|
||||||
func AesCfbDecrypt(encrypted, key []byte) []byte {
|
func AesCfbDecrypt(encrypted, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 16 && size != 24 && size != 32 {
|
||||||
|
panic("key length shoud be 16 or 24 or 32")
|
||||||
|
}
|
||||||
|
|
||||||
if len(encrypted) < aes.BlockSize {
|
if len(encrypted) < aes.BlockSize {
|
||||||
panic("encrypted data is too short")
|
panic("encrypted data is too short")
|
||||||
}
|
}
|
||||||
@@ -167,6 +192,11 @@ func AesCfbDecrypt(encrypted, key []byte) []byte {
|
|||||||
// len(key) should be 16, 24 or 32.
|
// len(key) should be 16, 24 or 32.
|
||||||
// Play: https://go.dev/play/p/VtHxtkUj-3F
|
// Play: https://go.dev/play/p/VtHxtkUj-3F
|
||||||
func AesOfbEncrypt(data, key []byte) []byte {
|
func AesOfbEncrypt(data, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 16 && size != 24 && size != 32 {
|
||||||
|
panic("key length shoud be 16 or 24 or 32")
|
||||||
|
}
|
||||||
|
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -189,6 +219,11 @@ func AesOfbEncrypt(data, key []byte) []byte {
|
|||||||
// len(key) should be 16, 24 or 32.
|
// len(key) should be 16, 24 or 32.
|
||||||
// Play: https://go.dev/play/p/VtHxtkUj-3F
|
// Play: https://go.dev/play/p/VtHxtkUj-3F
|
||||||
func AesOfbDecrypt(data, key []byte) []byte {
|
func AesOfbDecrypt(data, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 16 && size != 24 && size != 32 {
|
||||||
|
panic("key length shoud be 16 or 24 or 32")
|
||||||
|
}
|
||||||
|
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -255,6 +290,11 @@ func DesEcbDecrypt(encrypted, key []byte) []byte {
|
|||||||
// len(key) should be 8.
|
// len(key) should be 8.
|
||||||
// Play: https://go.dev/play/p/4cC4QvWfe3_1
|
// Play: https://go.dev/play/p/4cC4QvWfe3_1
|
||||||
func DesCbcEncrypt(data, key []byte) []byte {
|
func DesCbcEncrypt(data, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 8 {
|
||||||
|
panic("key length shoud be 8")
|
||||||
|
}
|
||||||
|
|
||||||
block, _ := des.NewCipher(key)
|
block, _ := des.NewCipher(key)
|
||||||
data = pkcs7Padding(data, block.BlockSize())
|
data = pkcs7Padding(data, block.BlockSize())
|
||||||
|
|
||||||
@@ -275,6 +315,11 @@ func DesCbcEncrypt(data, key []byte) []byte {
|
|||||||
// len(key) should be 8.
|
// len(key) should be 8.
|
||||||
// Play: https://go.dev/play/p/4cC4QvWfe3_1
|
// Play: https://go.dev/play/p/4cC4QvWfe3_1
|
||||||
func DesCbcDecrypt(encrypted, key []byte) []byte {
|
func DesCbcDecrypt(encrypted, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 8 {
|
||||||
|
panic("key length shoud be 8")
|
||||||
|
}
|
||||||
|
|
||||||
block, _ := des.NewCipher(key)
|
block, _ := des.NewCipher(key)
|
||||||
|
|
||||||
iv := encrypted[:des.BlockSize]
|
iv := encrypted[:des.BlockSize]
|
||||||
@@ -291,6 +336,11 @@ func DesCbcDecrypt(encrypted, key []byte) []byte {
|
|||||||
// len(key) should be 8.
|
// len(key) should be 8.
|
||||||
// Play: https://go.dev/play/p/9-T6OjKpcdw
|
// Play: https://go.dev/play/p/9-T6OjKpcdw
|
||||||
func DesCtrCrypt(data, key []byte) []byte {
|
func DesCtrCrypt(data, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 8 {
|
||||||
|
panic("key length shoud be 8")
|
||||||
|
}
|
||||||
|
|
||||||
block, _ := des.NewCipher(key)
|
block, _ := des.NewCipher(key)
|
||||||
|
|
||||||
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
||||||
@@ -306,6 +356,11 @@ func DesCtrCrypt(data, key []byte) []byte {
|
|||||||
// len(key) should be 8.
|
// len(key) should be 8.
|
||||||
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
||||||
func DesCfbEncrypt(data, key []byte) []byte {
|
func DesCfbEncrypt(data, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 8 {
|
||||||
|
panic("key length shoud be 8")
|
||||||
|
}
|
||||||
|
|
||||||
block, err := des.NewCipher(key)
|
block, err := des.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -327,6 +382,11 @@ func DesCfbEncrypt(data, key []byte) []byte {
|
|||||||
// len(encrypted) should be great than 16, len(key) should be 8.
|
// len(encrypted) should be great than 16, len(key) should be 8.
|
||||||
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
||||||
func DesCfbDecrypt(encrypted, key []byte) []byte {
|
func DesCfbDecrypt(encrypted, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 8 {
|
||||||
|
panic("key length shoud be 8")
|
||||||
|
}
|
||||||
|
|
||||||
block, _ := des.NewCipher(key)
|
block, _ := des.NewCipher(key)
|
||||||
if len(encrypted) < des.BlockSize {
|
if len(encrypted) < des.BlockSize {
|
||||||
panic("encrypted data is too short")
|
panic("encrypted data is too short")
|
||||||
@@ -341,9 +401,14 @@ func DesCfbDecrypt(encrypted, key []byte) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
|
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
|
||||||
// len(key) should be 16, 24 or 32.
|
// len(key) should be 8.
|
||||||
// Play: https://go.dev/play/p/74KmNadjN1J
|
// Play: https://go.dev/play/p/74KmNadjN1J
|
||||||
func DesOfbEncrypt(data, key []byte) []byte {
|
func DesOfbEncrypt(data, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 8 {
|
||||||
|
panic("key length shoud be 8")
|
||||||
|
}
|
||||||
|
|
||||||
block, err := des.NewCipher(key)
|
block, err := des.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -365,6 +430,11 @@ func DesOfbEncrypt(data, key []byte) []byte {
|
|||||||
// len(key) should be 8.
|
// len(key) should be 8.
|
||||||
// Play: https://go.dev/play/p/74KmNadjN1J
|
// Play: https://go.dev/play/p/74KmNadjN1J
|
||||||
func DesOfbDecrypt(data, key []byte) []byte {
|
func DesOfbDecrypt(data, key []byte) []byte {
|
||||||
|
size := len(key)
|
||||||
|
if size != 8 {
|
||||||
|
panic("key length shoud be 8")
|
||||||
|
}
|
||||||
|
|
||||||
block, err := des.NewCipher(key)
|
block, err := des.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
@@ -123,6 +123,25 @@ func (hm *HashMap) Iterate(iteratee func(key, value any)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilterByValue returns a filtered HashMap.
|
||||||
|
// If any value is not matching the perdicate function then it returns nil
|
||||||
|
// otherwise it returns the HashMap with selected values.
|
||||||
|
func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap {
|
||||||
|
var filteredHM *HashMap
|
||||||
|
if hm.size > 0 {
|
||||||
|
for i := 0; i < len(hm.table); i++ {
|
||||||
|
item := hm.table[i]
|
||||||
|
if item != nil && perdicate(item.value) {
|
||||||
|
if filteredHM == nil {
|
||||||
|
filteredHM = NewHashMap()
|
||||||
|
}
|
||||||
|
filteredHM.Put(item.key, item.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredHM
|
||||||
|
}
|
||||||
|
|
||||||
// Keys returns a slice of the hashmap's keys (random order)
|
// Keys returns a slice of the hashmap's keys (random order)
|
||||||
func (hm *HashMap) Keys() []any {
|
func (hm *HashMap) Keys() []any {
|
||||||
keys := make([]any, int(hm.size))
|
keys := make([]any, int(hm.size))
|
||||||
@@ -168,6 +187,11 @@ func (hm *HashMap) resize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size returns current size of Hashmap
|
||||||
|
func (hm *HashMap) Size() uint64 {
|
||||||
|
return hm.size
|
||||||
|
}
|
||||||
|
|
||||||
func (hm *HashMap) hash(key any) uint64 {
|
func (hm *HashMap) hash(key any) uint64 {
|
||||||
h := fnv.New64a()
|
h := fnv.New64a()
|
||||||
_, _ = h.Write([]byte(fmt.Sprintf("%v", key)))
|
_, _ = h.Write([]byte(fmt.Sprintf("%v", key)))
|
||||||
|
|||||||
@@ -105,3 +105,24 @@ func TestHashMap_GetOrDefault(t *testing.T) {
|
|||||||
assert.Equal(1, hm.GetOrDefault("a", 5))
|
assert.Equal(1, hm.GetOrDefault("a", 5))
|
||||||
assert.Equal(5, hm.GetOrDefault("d", 5))
|
assert.Equal(5, hm.GetOrDefault("d", 5))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHashMap_FilterByValue(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestHashMap_FilterByValue")
|
||||||
|
|
||||||
|
hm := NewHashMap()
|
||||||
|
|
||||||
|
hm.Put("a", 1)
|
||||||
|
hm.Put("b", 2)
|
||||||
|
hm.Put("c", 3)
|
||||||
|
hm.Put("d", 4)
|
||||||
|
hm.Put("e", 5)
|
||||||
|
hm.Put("f", 6)
|
||||||
|
|
||||||
|
filteredHM := hm.FilterByValue(func(value any) bool {
|
||||||
|
return value.(int) == 1 || value.(int) == 3
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(uint64(2), filteredHM.Size())
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,6 +90,35 @@ func lastIndexOf[T any](o T, e []T, start int, end int) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying the
|
||||||
|
// functional predicate f(T) bool
|
||||||
|
// if not found return -1.
|
||||||
|
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int {
|
||||||
|
index := -1
|
||||||
|
data := l.getList()
|
||||||
|
for i := len(data) - 1; i >= 0; i-- {
|
||||||
|
if f(data[i]) {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexOfFunc returns the first index satisfying the functional predicate f(v) bool
|
||||||
|
// if not found return -1.
|
||||||
|
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int {
|
||||||
|
index := -1
|
||||||
|
data := l.getList()
|
||||||
|
for i, v := range data {
|
||||||
|
if f(v) {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
// get returns the element at the specified position in this list.
|
// get returns the element at the specified position in this list.
|
||||||
func get[T any](o []T, index int) *T {
|
func get[T any](o []T, index int) *T {
|
||||||
return &o[index]
|
return &o[index]
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package datastructure
|
package datastructure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCopyOnWriteList_ValueOf(t *testing.T) {
|
func TestCopyOnWriteList_ValueOf(t *testing.T) {
|
||||||
@@ -233,3 +234,35 @@ func TestCopyOnWriteList_SubList(t *testing.T) {
|
|||||||
subList = list.SubList(11, 1)
|
subList = list.SubList(11, 1)
|
||||||
assert.Equal([]int{}, subList)
|
assert.Equal([]int{}, subList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCopyOnWriteListIndexOfFunc(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestIndexOfFunc")
|
||||||
|
|
||||||
|
list := NewCopyOnWriteList([]int{1, 2, 3})
|
||||||
|
i := list.IndexOfFunc(func(a int) bool { return a == 1 })
|
||||||
|
assert.Equal(0, i)
|
||||||
|
|
||||||
|
i = list.IndexOfFunc(func(a int) bool { return a == 4 })
|
||||||
|
assert.Equal(-1, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewCopyOnWriteListLastIndexOfFunc(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestLastIndexOfFunc")
|
||||||
|
|
||||||
|
list := NewCopyOnWriteList([]int{1, 2, 3, 3, 3, 3, 4, 5, 6, 9})
|
||||||
|
i := list.LastIndexOfFunc(func(a int) bool { return a == 3 })
|
||||||
|
assert.Equal(5, i)
|
||||||
|
|
||||||
|
i = list.LastIndexOfFunc(func(a int) bool { return a == 10 })
|
||||||
|
assert.Equal(-1, i)
|
||||||
|
|
||||||
|
i = list.LastIndexOfFunc(func(a int) bool { return a == 4 })
|
||||||
|
assert.Equal(6, i)
|
||||||
|
|
||||||
|
i = list.LastIndexOfFunc(func(a int) bool { return a == 1 })
|
||||||
|
assert.Equal(0, i)
|
||||||
|
}
|
||||||
|
|||||||
108
datastructure/optional/optional.go
Normal file
108
datastructure/optional/optional.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package optional
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Optional is a type that may or may not contain a non-nil value.
|
||||||
|
type Optional[T any] struct {
|
||||||
|
value *T
|
||||||
|
mu *sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default returns an default Optional instance.
|
||||||
|
func Default[T any]() Optional[T] {
|
||||||
|
return Optional[T]{mu: &sync.RWMutex{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of returns an Optional with a non-nil value.
|
||||||
|
func Of[T any](value T) Optional[T] {
|
||||||
|
return Optional[T]{value: &value, mu: &sync.RWMutex{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromNillable returns an Optional for a given value, which may be nil.
|
||||||
|
func FromNillable[T any](value *T) Optional[T] {
|
||||||
|
if value == nil {
|
||||||
|
return Default[T]()
|
||||||
|
}
|
||||||
|
return Optional[T]{value: value, mu: &sync.RWMutex{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotNil checks if there is a value present.
|
||||||
|
func (o Optional[T]) IsNotNil() bool {
|
||||||
|
o.mu.RLock()
|
||||||
|
defer o.mu.RUnlock()
|
||||||
|
|
||||||
|
return o.value != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNil checks if the Optional is nil.
|
||||||
|
func (o Optional[T]) IsNil() bool {
|
||||||
|
return !o.IsNotNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfNotNil performs the given action with the value if a value is not nil.
|
||||||
|
func (o Optional[T]) IfNotNil(action func(value T)) {
|
||||||
|
o.mu.RLock()
|
||||||
|
defer o.mu.RUnlock()
|
||||||
|
|
||||||
|
if o.value != nil {
|
||||||
|
action(*o.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfNotNilOrElse performs the action with the value if present, otherwise performs the fallback action.
|
||||||
|
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func()) {
|
||||||
|
o.mu.RLock()
|
||||||
|
defer o.mu.RUnlock()
|
||||||
|
|
||||||
|
if o.value != nil {
|
||||||
|
action(*o.value)
|
||||||
|
} else {
|
||||||
|
fallbackAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwarp returns the value if not nil, otherwise panics.
|
||||||
|
func (o Optional[T]) Unwarp() T {
|
||||||
|
o.mu.RLock()
|
||||||
|
defer o.mu.RUnlock()
|
||||||
|
|
||||||
|
if o.value == nil {
|
||||||
|
panic("Optional.Get: no value present")
|
||||||
|
}
|
||||||
|
return *o.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrElse returns the value if is not nil, otherwise returns other.
|
||||||
|
func (o Optional[T]) OrElse(other T) T {
|
||||||
|
o.mu.RLock()
|
||||||
|
defer o.mu.RUnlock()
|
||||||
|
|
||||||
|
if o.value != nil {
|
||||||
|
return *o.value
|
||||||
|
}
|
||||||
|
return other
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrElseGet returns the value if is not nil, otherwise invokes action and returns the result.
|
||||||
|
func (o Optional[T]) OrElseGet(action func() T) T {
|
||||||
|
o.mu.RLock()
|
||||||
|
defer o.mu.RUnlock()
|
||||||
|
|
||||||
|
if o.value != nil {
|
||||||
|
return *o.value
|
||||||
|
}
|
||||||
|
return action()
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrElseTrigger returns the value if present, otherwise returns an error.
|
||||||
|
func (o Optional[T]) OrElseTrigger(errorHandler func() error) (T, error) {
|
||||||
|
o.mu.RLock()
|
||||||
|
defer o.mu.RUnlock()
|
||||||
|
|
||||||
|
if o.value == nil {
|
||||||
|
return *new(T), errorHandler()
|
||||||
|
}
|
||||||
|
return *o.value, nil
|
||||||
|
}
|
||||||
151
datastructure/optional/optional_test.go
Normal file
151
datastructure/optional/optional_test.go
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
package optional
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDefault(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestEmpty")
|
||||||
|
opt := Default[int]()
|
||||||
|
|
||||||
|
assert.ShouldBeTrue(opt.IsNil())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOf(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestOf")
|
||||||
|
value := 42
|
||||||
|
opt := Of(value)
|
||||||
|
|
||||||
|
assert.ShouldBeTrue(opt.IsNotNil())
|
||||||
|
assert.Equal(opt.Unwarp(), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromNillable(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestOfNullable")
|
||||||
|
var value *int = nil
|
||||||
|
opt := FromNillable(value)
|
||||||
|
|
||||||
|
assert.ShouldBeFalse(opt.IsNotNil())
|
||||||
|
|
||||||
|
value = new(int)
|
||||||
|
*value = 42
|
||||||
|
opt = FromNillable(value)
|
||||||
|
|
||||||
|
assert.ShouldBeTrue(opt.IsNotNil())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrElse(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestOrElse")
|
||||||
|
optDefault := Default[int]()
|
||||||
|
defaultValue := 100
|
||||||
|
|
||||||
|
val := optDefault.OrElse(defaultValue)
|
||||||
|
assert.Equal(val, defaultValue)
|
||||||
|
|
||||||
|
optWithValue := Of(42)
|
||||||
|
val = optWithValue.OrElse(defaultValue)
|
||||||
|
assert.Equal(val, 42)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrElseGetHappyPath(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestOrElseGetHappyPath")
|
||||||
|
optWithValue := Of(42)
|
||||||
|
action := func() int { return 100 }
|
||||||
|
|
||||||
|
val := optWithValue.OrElseGet(action)
|
||||||
|
assert.Equal(val, 42)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrElseGet(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestOrElseGet")
|
||||||
|
optDefault := Default[int]()
|
||||||
|
action := func() int { return 100 }
|
||||||
|
|
||||||
|
val := optDefault.OrElseGet(action)
|
||||||
|
assert.Equal(val, action())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrElseTrigger(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "OrElseTrigger")
|
||||||
|
optDefault := Default[int]()
|
||||||
|
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
|
||||||
|
|
||||||
|
assert.Equal(err.Error(), "no value")
|
||||||
|
|
||||||
|
optWithValue := Of(42)
|
||||||
|
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(val, 42)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIfNotNil(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "IfNotNil")
|
||||||
|
called := false
|
||||||
|
action := func(value int) { called = true }
|
||||||
|
|
||||||
|
optDefault := Default[int]()
|
||||||
|
optDefault.IfNotNil(action)
|
||||||
|
|
||||||
|
assert.ShouldBeFalse(called)
|
||||||
|
|
||||||
|
called = false // Reset for next test
|
||||||
|
optWithValue := Of(42)
|
||||||
|
optWithValue.IfNotNil(action)
|
||||||
|
|
||||||
|
assert.ShouldBeTrue(called)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIfNotNilOrElse(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestIfNotNilOrElse")
|
||||||
|
|
||||||
|
// Test when value is present
|
||||||
|
calledWithValue := false
|
||||||
|
valueAction := func(value int) { calledWithValue = true }
|
||||||
|
fallbackAction := func() { t.Errorf("Empty action should not be called when value is present") }
|
||||||
|
|
||||||
|
optWithValue := Of(42)
|
||||||
|
optWithValue.IfNotNilOrElse(valueAction, fallbackAction)
|
||||||
|
|
||||||
|
assert.ShouldBeTrue(calledWithValue)
|
||||||
|
|
||||||
|
// Test when value is not present
|
||||||
|
calledWithEmpty := false
|
||||||
|
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
|
||||||
|
fallbackAction = func() { calledWithEmpty = true }
|
||||||
|
|
||||||
|
optDefault := Default[int]()
|
||||||
|
optDefault.IfNotNilOrElse(valueAction, fallbackAction)
|
||||||
|
|
||||||
|
assert.ShouldBeTrue(calledWithEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetWithPanicStandard(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestGetWithPanicStandard")
|
||||||
|
|
||||||
|
// Test when value is present
|
||||||
|
optWithValue := Of(42)
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
assert.IsNil(r)
|
||||||
|
}()
|
||||||
|
val := optWithValue.Unwarp()
|
||||||
|
if val != 42 {
|
||||||
|
t.Errorf("Expected Unwarp to return 42, got %v", val)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Test when value is not present
|
||||||
|
optDefault := Default[int]()
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
assert.IsNotNil(r)
|
||||||
|
}()
|
||||||
|
_ = optDefault.Unwarp()
|
||||||
|
}()
|
||||||
|
}
|
||||||
@@ -4,19 +4,21 @@
|
|||||||
// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate.
|
// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate.
|
||||||
package datastructure
|
package datastructure
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
// Set is a data container, like slice, but element of set is not duplicate.
|
// Set is a data container, like slice, but element of set is not duplicate.
|
||||||
type Set[T comparable] map[T]struct{}
|
type Set[T comparable] map[T]struct{}
|
||||||
|
|
||||||
// NewSet return a instance of set
|
// New create a instance of set from given values.
|
||||||
func NewSet[T comparable](items ...T) Set[T] {
|
func New[T comparable](items ...T) Set[T] {
|
||||||
set := make(Set[T])
|
set := make(Set[T], len(items))
|
||||||
set.Add(items...)
|
set.Add(items...)
|
||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSetFromSlice create a set from slice
|
// FromSlice create a set from given slice.
|
||||||
func NewSetFromSlice[T comparable](items []T) Set[T] {
|
func FromSlice[T comparable](items []T) Set[T] {
|
||||||
set := make(Set[T])
|
set := make(Set[T], len(items))
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
set.Add(item)
|
set.Add(item)
|
||||||
}
|
}
|
||||||
@@ -77,8 +79,7 @@ func (s Set[T]) ContainAll(other Set[T]) bool {
|
|||||||
|
|
||||||
// Clone return a copy of set
|
// Clone return a copy of set
|
||||||
func (s Set[T]) Clone() Set[T] {
|
func (s Set[T]) Clone() Set[T] {
|
||||||
set := NewSet[T]()
|
set := FromSlice(s.ToSlice())
|
||||||
set.Add(s.Values()...)
|
|
||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,14 +117,11 @@ func (s Set[T]) Size() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Values return all values of set
|
// Values return all values of set
|
||||||
|
// Deprecated: Values function is deprecated and will be removed in future versions. Please use ToSlice() function instead.
|
||||||
|
//
|
||||||
|
// The ToSlice() function provides the same functionality as Values and returns a slice containing all values of the set.
|
||||||
func (s Set[T]) Values() []T {
|
func (s Set[T]) Values() []T {
|
||||||
result := make([]T, 0, len(s))
|
return s.ToSlice()
|
||||||
|
|
||||||
s.Iterate(func(value T) {
|
|
||||||
result = append(result, value)
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Union creates a new set contain all element of set s and other
|
// Union creates a new set contain all element of set s and other
|
||||||
@@ -135,7 +133,7 @@ func (s Set[T]) Union(other Set[T]) Set[T] {
|
|||||||
|
|
||||||
// Intersection creates a new set whose element both be contained in set s and other
|
// Intersection creates a new set whose element both be contained in set s and other
|
||||||
func (s Set[T]) Intersection(other Set[T]) Set[T] {
|
func (s Set[T]) Intersection(other Set[T]) Set[T] {
|
||||||
set := NewSet[T]()
|
set := New[T]()
|
||||||
s.Iterate(func(value T) {
|
s.Iterate(func(value T) {
|
||||||
if other.Contain(value) {
|
if other.Contain(value) {
|
||||||
set.Add(value)
|
set.Add(value)
|
||||||
@@ -147,7 +145,7 @@ func (s Set[T]) Intersection(other Set[T]) Set[T] {
|
|||||||
|
|
||||||
// SymmetricDifference creates a new set whose element is in set1 or set2, but not in both sets
|
// SymmetricDifference creates a new set whose element is in set1 or set2, but not in both sets
|
||||||
func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
|
func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
|
||||||
set := NewSet[T]()
|
set := New[T]()
|
||||||
s.Iterate(func(value T) {
|
s.Iterate(func(value T) {
|
||||||
if !other.Contain(value) {
|
if !other.Contain(value) {
|
||||||
set.Add(value)
|
set.Add(value)
|
||||||
@@ -163,9 +161,9 @@ func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
|
|||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minus creates an set of whose element in origin set but not in compared set
|
// Minus creates a set of whose element in origin set but not in compared set
|
||||||
func (s Set[T]) Minus(comparedSet Set[T]) Set[T] {
|
func (s Set[T]) Minus(comparedSet Set[T]) Set[T] {
|
||||||
set := NewSet[T]()
|
set := New[T]()
|
||||||
|
|
||||||
s.Iterate(func(value T) {
|
s.Iterate(func(value T) {
|
||||||
if !comparedSet.Contain(value) {
|
if !comparedSet.Contain(value) {
|
||||||
@@ -189,11 +187,34 @@ func (s Set[T]) EachWithBreak(iteratee func(item T) bool) {
|
|||||||
// Pop delete the top element of set then return it, if set is empty, return nil-value of T and false.
|
// Pop delete the top element of set then return it, if set is empty, return nil-value of T and false.
|
||||||
func (s Set[T]) Pop() (v T, ok bool) {
|
func (s Set[T]) Pop() (v T, ok bool) {
|
||||||
if len(s) > 0 {
|
if len(s) > 0 {
|
||||||
items := s.Values()
|
for item := range s {
|
||||||
item := items[len(s)-1]
|
v = item
|
||||||
delete(s, item)
|
delete(s, item)
|
||||||
return item, true
|
return v, true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return v, false
|
return v, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToSlice returns a slice containing all values of the set.
|
||||||
|
func (s Set[T]) ToSlice() []T {
|
||||||
|
if s.IsEmpty() {
|
||||||
|
return []T{}
|
||||||
|
}
|
||||||
|
result := make([]T, 0, s.Size())
|
||||||
|
s.Iterate(func(value T) {
|
||||||
|
result = append(result, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSortedSlice returns a sorted slice containing all values of the set.
|
||||||
|
func (s Set[T]) ToSortedSlice(less func(v1, v2 T) bool) []T {
|
||||||
|
result := s.ToSlice()
|
||||||
|
sort.Slice(result, func(i, j int) bool {
|
||||||
|
return less(result[i], result[j])
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
package datastructure
|
package datastructure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSet_NewSetFromSlice(t *testing.T) {
|
func TestSet_FromSlice(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_NewSetFromSlice")
|
assert := internal.NewAssert(t, "TestSet_FromSlice")
|
||||||
|
|
||||||
s1 := NewSetFromSlice([]int{1, 2, 2, 3})
|
s1 := FromSlice([]int{1, 2, 2, 3})
|
||||||
assert.Equal(3, s1.Size())
|
assert.Equal(3, s1.Size())
|
||||||
assert.Equal(true, s1.Contain(1))
|
assert.Equal(true, s1.Contain(1))
|
||||||
assert.Equal(true, s1.Contain(2))
|
assert.Equal(true, s1.Contain(2))
|
||||||
assert.Equal(true, s1.Contain(3))
|
assert.Equal(true, s1.Contain(3))
|
||||||
|
|
||||||
s2 := NewSetFromSlice([]int{})
|
s2 := FromSlice([]int{})
|
||||||
assert.Equal(0, s2.Size())
|
assert.Equal(0, s2.Size())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,10 +28,10 @@ func TestSet_Add(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Add")
|
assert := internal.NewAssert(t, "TestSet_Add")
|
||||||
|
|
||||||
set := NewSet[int]()
|
set := New[int]()
|
||||||
set.Add(1, 2, 3)
|
set.Add(1, 2, 3)
|
||||||
|
|
||||||
cmpSet := NewSet(1, 2, 3)
|
cmpSet := New(1, 2, 3)
|
||||||
|
|
||||||
assert.Equal(true, set.Equal(cmpSet))
|
assert.Equal(true, set.Equal(cmpSet))
|
||||||
}
|
}
|
||||||
@@ -39,12 +41,12 @@ func TestSet_AddIfNotExist(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_AddIfNotExist")
|
assert := internal.NewAssert(t, "TestSet_AddIfNotExist")
|
||||||
|
|
||||||
set := NewSet[int]()
|
set := New[int]()
|
||||||
set.Add(1, 2, 3)
|
set.Add(1, 2, 3)
|
||||||
|
|
||||||
assert.Equal(false, set.AddIfNotExist(1))
|
assert.Equal(false, set.AddIfNotExist(1))
|
||||||
assert.Equal(true, set.AddIfNotExist(4))
|
assert.Equal(true, set.AddIfNotExist(4))
|
||||||
assert.Equal(NewSet(1, 2, 3, 4), set)
|
assert.Equal(New(1, 2, 3, 4), set)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSet_AddIfNotExistBy(t *testing.T) {
|
func TestSet_AddIfNotExistBy(t *testing.T) {
|
||||||
@@ -52,7 +54,7 @@ func TestSet_AddIfNotExistBy(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_AddIfNotExistBy")
|
assert := internal.NewAssert(t, "TestSet_AddIfNotExistBy")
|
||||||
|
|
||||||
set := NewSet[int]()
|
set := New[int]()
|
||||||
set.Add(1, 2)
|
set.Add(1, 2)
|
||||||
|
|
||||||
ok := set.AddIfNotExistBy(3, func(val int) bool {
|
ok := set.AddIfNotExistBy(3, func(val int) bool {
|
||||||
@@ -75,7 +77,7 @@ func TestSet_Contain(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Contain")
|
assert := internal.NewAssert(t, "TestSet_Contain")
|
||||||
|
|
||||||
set := NewSet[int]()
|
set := New[int]()
|
||||||
set.Add(1, 2, 3)
|
set.Add(1, 2, 3)
|
||||||
|
|
||||||
assert.Equal(true, set.Contain(1))
|
assert.Equal(true, set.Contain(1))
|
||||||
@@ -87,9 +89,9 @@ func TestSet_ContainAll(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_ContainAll")
|
assert := internal.NewAssert(t, "TestSet_ContainAll")
|
||||||
|
|
||||||
set1 := NewSet(1, 2, 3)
|
set1 := New(1, 2, 3)
|
||||||
set2 := NewSet(1, 2)
|
set2 := New(1, 2)
|
||||||
set3 := NewSet(1, 2, 3, 4)
|
set3 := New(1, 2, 3, 4)
|
||||||
|
|
||||||
assert.Equal(true, set1.ContainAll(set2))
|
assert.Equal(true, set1.ContainAll(set2))
|
||||||
assert.Equal(false, set1.ContainAll(set3))
|
assert.Equal(false, set1.ContainAll(set3))
|
||||||
@@ -100,7 +102,7 @@ func TestSet_Clone(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Clone")
|
assert := internal.NewAssert(t, "TestSet_Clone")
|
||||||
|
|
||||||
set1 := NewSet(1, 2, 3)
|
set1 := New(1, 2, 3)
|
||||||
set2 := set1.Clone()
|
set2 := set1.Clone()
|
||||||
|
|
||||||
assert.Equal(true, set1.Size() == set2.Size())
|
assert.Equal(true, set1.Size() == set2.Size())
|
||||||
@@ -112,11 +114,11 @@ func TestSet_Delete(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Delete")
|
assert := internal.NewAssert(t, "TestSet_Delete")
|
||||||
|
|
||||||
set := NewSet[int]()
|
set := New[int]()
|
||||||
set.Add(1, 2, 3)
|
set.Add(1, 2, 3)
|
||||||
set.Delete(3)
|
set.Delete(3)
|
||||||
|
|
||||||
assert.Equal(true, set.Equal(NewSet(1, 2)))
|
assert.Equal(true, set.Equal(New(1, 2)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSet_Equal(t *testing.T) {
|
func TestSet_Equal(t *testing.T) {
|
||||||
@@ -124,9 +126,9 @@ func TestSet_Equal(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Equal")
|
assert := internal.NewAssert(t, "TestSet_Equal")
|
||||||
|
|
||||||
set1 := NewSet(1, 2, 3)
|
set1 := New(1, 2, 3)
|
||||||
set2 := NewSet(1, 2, 3)
|
set2 := New(1, 2, 3)
|
||||||
set3 := NewSet(1, 2, 3, 4)
|
set3 := New(1, 2, 3, 4)
|
||||||
|
|
||||||
assert.Equal(true, set1.Equal(set2))
|
assert.Equal(true, set1.Equal(set2))
|
||||||
assert.Equal(false, set1.Equal(set3))
|
assert.Equal(false, set1.Equal(set3))
|
||||||
@@ -137,7 +139,7 @@ func TestSet_Iterate(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Iterate")
|
assert := internal.NewAssert(t, "TestSet_Iterate")
|
||||||
|
|
||||||
set := NewSet(1, 2, 3)
|
set := New(1, 2, 3)
|
||||||
arr := []int{}
|
arr := []int{}
|
||||||
set.Iterate(func(value int) {
|
set.Iterate(func(value int) {
|
||||||
arr = append(arr, value)
|
arr = append(arr, value)
|
||||||
@@ -151,7 +153,7 @@ func TestSet_IsEmpty(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_IsEmpty")
|
assert := internal.NewAssert(t, "TestSet_IsEmpty")
|
||||||
|
|
||||||
set := NewSet[int]()
|
set := New[int]()
|
||||||
assert.Equal(true, set.IsEmpty())
|
assert.Equal(true, set.IsEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +162,7 @@ func TestSet_Size(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Size")
|
assert := internal.NewAssert(t, "TestSet_Size")
|
||||||
|
|
||||||
set := NewSet(1, 2, 3)
|
set := New(1, 2, 3)
|
||||||
assert.Equal(3, set.Size())
|
assert.Equal(3, set.Size())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +171,7 @@ func TestSet_Values(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Values")
|
assert := internal.NewAssert(t, "TestSet_Values")
|
||||||
|
|
||||||
set := NewSet(1, 2, 3)
|
set := New(1, 2, 3)
|
||||||
values := set.Values()
|
values := set.Values()
|
||||||
|
|
||||||
assert.Equal(3, len(values))
|
assert.Equal(3, len(values))
|
||||||
@@ -180,12 +182,12 @@ func TestSet_Union(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Union")
|
assert := internal.NewAssert(t, "TestSet_Union")
|
||||||
|
|
||||||
set1 := NewSet(1, 2, 3)
|
set1 := New(1, 2, 3)
|
||||||
set2 := NewSet(2, 3, 4, 5)
|
set2 := New(2, 3, 4, 5)
|
||||||
|
|
||||||
unionSet := set1.Union(set2)
|
unionSet := set1.Union(set2)
|
||||||
|
|
||||||
assert.Equal(NewSet(1, 2, 3, 4, 5), unionSet)
|
assert.Equal(New(1, 2, 3, 4, 5), unionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSet_Intersection(t *testing.T) {
|
func TestSet_Intersection(t *testing.T) {
|
||||||
@@ -193,11 +195,11 @@ func TestSet_Intersection(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Intersection")
|
assert := internal.NewAssert(t, "TestSet_Intersection")
|
||||||
|
|
||||||
set1 := NewSet(1, 2, 3)
|
set1 := New(1, 2, 3)
|
||||||
set2 := NewSet(2, 3, 4, 5)
|
set2 := New(2, 3, 4, 5)
|
||||||
intersectionSet := set1.Intersection(set2)
|
intersectionSet := set1.Intersection(set2)
|
||||||
|
|
||||||
assert.Equal(NewSet(2, 3), intersectionSet)
|
assert.Equal(New(2, 3), intersectionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSet_SymmetricDifference(t *testing.T) {
|
func TestSet_SymmetricDifference(t *testing.T) {
|
||||||
@@ -205,10 +207,10 @@ func TestSet_SymmetricDifference(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_SymmetricDifference")
|
assert := internal.NewAssert(t, "TestSet_SymmetricDifference")
|
||||||
|
|
||||||
set1 := NewSet(1, 2, 3)
|
set1 := New(1, 2, 3)
|
||||||
set2 := NewSet(2, 3, 4, 5)
|
set2 := New(2, 3, 4, 5)
|
||||||
|
|
||||||
assert.Equal(NewSet(1, 4, 5), set1.SymmetricDifference(set2))
|
assert.Equal(New(1, 4, 5), set1.SymmetricDifference(set2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSet_Minus(t *testing.T) {
|
func TestSet_Minus(t *testing.T) {
|
||||||
@@ -216,16 +218,16 @@ func TestSet_Minus(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSet_Minus")
|
assert := internal.NewAssert(t, "TestSet_Minus")
|
||||||
|
|
||||||
set1 := NewSet(1, 2, 3)
|
set1 := New(1, 2, 3)
|
||||||
set2 := NewSet(2, 3, 4, 5)
|
set2 := New(2, 3, 4, 5)
|
||||||
set3 := NewSet(2, 3)
|
set3 := New(2, 3)
|
||||||
|
|
||||||
assert.Equal(NewSet(1), set1.Minus(set2))
|
assert.Equal(New(1), set1.Minus(set2))
|
||||||
assert.Equal(NewSet(4, 5), set2.Minus(set3))
|
assert.Equal(New(4, 5), set2.Minus(set3))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEachWithBreak(t *testing.T) {
|
func TestEachWithBreak(t *testing.T) {
|
||||||
// s := NewSet(1, 2, 3, 4, 5)
|
// s := New(1, 2, 3, 4, 5)
|
||||||
|
|
||||||
// var sum int
|
// var sum int
|
||||||
|
|
||||||
@@ -241,22 +243,93 @@ func TestEachWithBreak(t *testing.T) {
|
|||||||
// assert.Equal(6, sum)
|
// assert.Equal(6, sum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func TestPop(t *testing.T) {
|
func TestPop(t *testing.T) {
|
||||||
// assert := internal.NewAssert(t, "TestPop")
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestSet_Pop")
|
||||||
|
|
||||||
// s := NewSet[int]()
|
s := New[int]()
|
||||||
|
|
||||||
// val, ok := s.Pop()
|
val, ok := s.Pop()
|
||||||
// assert.Equal(0, val)
|
assert.Equal(0, val)
|
||||||
// assert.Equal(false, ok)
|
assert.Equal(false, ok)
|
||||||
|
|
||||||
// s.Add(1)
|
s = New(1, 2, 3, 4, 5)
|
||||||
// s.Add(2)
|
sl := s.ToSlice()
|
||||||
// s.Add(3)
|
|
||||||
|
|
||||||
// // s = NewSet(1, 2, 3, 4, 5)
|
val, ok = s.Pop()
|
||||||
|
assert.Equal(false, s.Contain(val))
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
assert.Equal(len(sl)-1, s.Size())
|
||||||
|
|
||||||
// val, ok = s.Pop()
|
var found bool
|
||||||
// assert.Equal(3, val)
|
|
||||||
// assert.Equal(true, ok)
|
for _, v := range sl {
|
||||||
// }
|
if v == val {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(true, found)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSet_ToSlice(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestSet_ToSlice")
|
||||||
|
|
||||||
|
set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1})
|
||||||
|
set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0})
|
||||||
|
set3 := New[string]()
|
||||||
|
|
||||||
|
slice1 := set1.ToSlice()
|
||||||
|
slice2 := set2.ToSlice()
|
||||||
|
slice3 := set3.ToSlice()
|
||||||
|
|
||||||
|
sort.Ints(slice1)
|
||||||
|
sort.Float64s(slice2)
|
||||||
|
|
||||||
|
assert.Equal(5, len(slice1))
|
||||||
|
assert.Equal(4, len(slice2))
|
||||||
|
assert.Equal(0, len(slice3))
|
||||||
|
|
||||||
|
assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7}))
|
||||||
|
assert.Equal(true, reflect.DeepEqual(slice2, []float64{-2.65, 0, 1.11, 4.25}))
|
||||||
|
assert.Equal("[]string", reflect.TypeOf(slice3).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSet_ToSortedSlice(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestSet_ToSortedSlice")
|
||||||
|
|
||||||
|
set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1})
|
||||||
|
set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0})
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
set3 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
|
||||||
|
|
||||||
|
slice1 := set1.ToSortedSlice(func(v1, v2 int) bool {
|
||||||
|
return v1 < v2
|
||||||
|
})
|
||||||
|
slice2 := set2.ToSortedSlice(func(v1, v2 float64) bool {
|
||||||
|
return v2 < v1
|
||||||
|
})
|
||||||
|
slice3 := set3.ToSortedSlice(func(v1, v2 Person) bool {
|
||||||
|
return v1.Age < v2.Age
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(5, len(slice1))
|
||||||
|
assert.Equal(4, len(slice2))
|
||||||
|
assert.Equal(3, len(slice3))
|
||||||
|
|
||||||
|
assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7}))
|
||||||
|
assert.Equal(true, reflect.DeepEqual(slice2, []float64{4.25, 1.11, 0, -2.65}))
|
||||||
|
assert.Equal(true, reflect.DeepEqual(slice3, []Person{
|
||||||
|
{"Jerry", 18},
|
||||||
|
{"Tom", 20},
|
||||||
|
{"Spike", 25},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|||||||
@@ -381,3 +381,12 @@ func TimestampNano(timezone ...string) int64 {
|
|||||||
|
|
||||||
return t.UnixNano()
|
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()
|
||||||
|
return func() {
|
||||||
|
elapsed := time.Since(pre)
|
||||||
|
fmt.Println("Costs Time:\t", elapsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export const slugify = (str: string): string =>
|
|||||||
export const commonConfig = defineConfig({
|
export const commonConfig = defineConfig({
|
||||||
title: 'Lancet',
|
title: 'Lancet',
|
||||||
appearance: true,
|
appearance: true,
|
||||||
|
ignoreDeadLinks: true,
|
||||||
|
|
||||||
markdown: {
|
markdown: {
|
||||||
theme: {
|
theme: {
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ import (
|
|||||||
- [ToInterface](#ToInterface)
|
- [ToInterface](#ToInterface)
|
||||||
- [Utf8ToGbk](#Utf8ToGbk)
|
- [Utf8ToGbk](#Utf8ToGbk)
|
||||||
- [GbkToUtf8](#GbkToUtf8)
|
- [GbkToUtf8](#GbkToUtf8)
|
||||||
|
- [ToStdBase64](#ToStdBase64)
|
||||||
|
- [ToUrlBase64](#ToUrlBase64)
|
||||||
|
- [ToRawStdBase64](#ToRawStdBase64)
|
||||||
|
- [ToRawUrlBase64](#ToRawUrlBase64)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -876,3 +880,272 @@ func main() {
|
|||||||
// hello
|
// hello
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="ToStdBase64">ToStdBase64</span>
|
||||||
|
|
||||||
|
<p>将值转换为StdBase64编码的字符串。error类型的数据也会把error的原因进行编码,复杂的结构会转为JSON格式的字符串</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToStdBase64(value any) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/_fLJqJD3NMo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
afterEncode := convertor.ToStdBase64(nil)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
afterEncode = convertor.ToStdBase64("")
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode = convertor.ToStdBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = convertor.ToStdBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = convertor.ToStdBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = convertor.ToStdBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = convertor.ToStdBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = convertor.ToStdBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = convertor.ToStdBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// aGVsbG8=
|
||||||
|
// aGVsbG8=
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||||
|
// MTIzLjQ1Ng==
|
||||||
|
// dHJ1ZQ==
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToUrlBase64">ToUrlBase64</span>
|
||||||
|
|
||||||
|
<p>值转换为 ToUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToUrlBase64(value any) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/C_d0GlvEeUR)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
afterEncode := convertor.ToUrlBase64(nil)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode = convertor.ToUrlBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = convertor.ToUrlBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = convertor.ToUrlBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = convertor.ToUrlBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = convertor.ToUrlBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = convertor.ToUrlBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = convertor.ToUrlBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
//
|
||||||
|
// aGVsbG8=
|
||||||
|
// aGVsbG8=
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||||
|
// MTIzLjQ1Ng==
|
||||||
|
// dHJ1ZQ==
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToRawStdBase64">ToRawStdBase64</span>
|
||||||
|
|
||||||
|
<p>值转换为 ToRawStdBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToRawStdBase64(value any) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wSAr3sfkDcv)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode = convertor.ToRawStdBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = convertor.ToRawStdBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = convertor.ToRawStdBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode := convertor.ToRawStdBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = convertor.ToRawStdBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = convertor.ToRawStdBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// aGVsbG8
|
||||||
|
// aGVsbG8
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||||
|
// MTIzLjQ1Ng
|
||||||
|
// dHJ1ZQ
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToRawUrlBase64">ToRawUrlBase64</span>
|
||||||
|
|
||||||
|
<p>值转换为 ToRawUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
|
||||||
|
|
||||||
|
<b>函数签名:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/HwdDPFcza1O)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToRawUrlBase64(value any) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode := convertor.ToRawUrlBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// aGVsbG8
|
||||||
|
// aGVsbG8
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||||
|
// MTIzLjQ1Ng
|
||||||
|
// dHJ1ZQ
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -26,6 +26,8 @@ import (
|
|||||||
- [Remove](#Remove)
|
- [Remove](#Remove)
|
||||||
- [IndexOf](#IndexOf)
|
- [IndexOf](#IndexOf)
|
||||||
- [LastIndexOf](#LastIndexOf)
|
- [LastIndexOf](#LastIndexOf)
|
||||||
|
- [IndexOfFunc](#IndexOfFunc)
|
||||||
|
- [LastIndexOfFunc](#LastIndexOfFunc)
|
||||||
- [IsEmpty](#IsEmpty)
|
- [IsEmpty](#IsEmpty)
|
||||||
- [Contain](#Contain)
|
- [Contain](#Contain)
|
||||||
- [ValueOf](#ValueOf)
|
- [ValueOf](#ValueOf)
|
||||||
@@ -198,6 +200,58 @@ func main() {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="IndexOfFunc">IndexOfFunc</span>
|
||||||
|
<p>返回第一个满足判断函数f(v)的元素的索引,如果找不到则返回-1。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
l := list.NewCopyOnWriteList([]int{1, 2, 3})
|
||||||
|
|
||||||
|
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 1 })) //0
|
||||||
|
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 0 })) //-1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="LastIndexOfFunc">LastIndexOfFunc</span>
|
||||||
|
<p>返回最后一个满足判断函数f(v)的元素的索引,如果找不到则返回-1。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
l := list.NewCopyOnWriteList([]int{1, 2, 3, 1})
|
||||||
|
|
||||||
|
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3
|
||||||
|
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### IsEmpty
|
### IsEmpty
|
||||||
|
|
||||||
如果此列表不包含任何元素,则返回 true。
|
如果此列表不包含任何元素,则返回 true。
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import (
|
|||||||
- [Iterate](#Iterate)
|
- [Iterate](#Iterate)
|
||||||
- [Keys](#Keys)
|
- [Keys](#Keys)
|
||||||
- [Values](#Values)
|
- [Values](#Values)
|
||||||
|
- [FilterByValue](#FilterByValue)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -276,7 +277,7 @@ func main() {
|
|||||||
|
|
||||||
### <span id="Values">Values</span>
|
### <span id="Values">Values</span>
|
||||||
|
|
||||||
<p>返回hashmap所有值的切片 (随机顺序).</p>
|
<p>返回hashmap所有值的切片 (随机顺序)。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -306,3 +307,40 @@ func main() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="FilterByValue">FilterByValue</span>
|
||||||
|
|
||||||
|
<p>返回一个过滤后的HashMap。 如果任何值与 perdicate 函数不匹配,则返回 nil,否则返回包含选定值的 HashMap。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
hm := hashmap.NewHashMap()
|
||||||
|
|
||||||
|
hm.Put("a", 1)
|
||||||
|
hm.Put("b", 2)
|
||||||
|
hm.Put("c", 3)
|
||||||
|
hm.Put("d", 4)
|
||||||
|
hm.Put("e", 5)
|
||||||
|
hm.Put("f", 6)
|
||||||
|
|
||||||
|
filteredHM := hm.FilterByValue(func(value any) bool {
|
||||||
|
return value.(int) == 1 || value.(int) == 3
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(filteredHM.Size()) //2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
412
docs/api/packages/datastructure/optional.md
Normal file
412
docs/api/packages/datastructure/optional.md
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
# Optional
|
||||||
|
Optional类型代表一个可选的值,它要么包含一个实际值,要么为空。
|
||||||
|
|
||||||
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
## 源码
|
||||||
|
|
||||||
|
- [https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go](https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go)
|
||||||
|
|
||||||
|
|
||||||
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
## 用法
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
- [Of](#Of)
|
||||||
|
- [FromNillable](#FromNillable)
|
||||||
|
- [Default](#Default)
|
||||||
|
- [IsNotNil](#IsNotNil)
|
||||||
|
- [IsNil](#IsNil)
|
||||||
|
- [IsNotNil](#IsNotNil)
|
||||||
|
- [IfNotNilOrElse](#IfNotNilOrElse)
|
||||||
|
- [Umwarp](#Umwarp)
|
||||||
|
- [OrElse](#OrElse)
|
||||||
|
- [OrElseGet](#OrElseGet)
|
||||||
|
- [OrElseTrigger](#OrElseTrigger)
|
||||||
|
|
||||||
|
|
||||||
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
## 文档
|
||||||
|
|
||||||
|
### <span id="Of">Of</span>
|
||||||
|
<p>返回一个包含非空值的Optional。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Of[T any](value T) Optional[T]
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
value := 42
|
||||||
|
opt := optional.Of(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.Get())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 42
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="FromNillable">FromNillable</span>
|
||||||
|
<p>返回一个包含给定值的Optional,该值可能为空 (nil)。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func FromNillable[T any](value *T) Optional[T]
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var value *int = nil
|
||||||
|
opt := optional.FromNillable(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.IsNotNil())
|
||||||
|
|
||||||
|
value = new(int)
|
||||||
|
*value = 42
|
||||||
|
opt = optional.FromNillable(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.IsNotNil())
|
||||||
|
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="Default">Default</span>
|
||||||
|
<p>返回一个空Optional实例。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Default[T any]() Optional[T]
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
fmt.Println(optDefault.IsNil())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="IsNil">IsNil</span>
|
||||||
|
<p>验证Optional是否为空。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) IsNil() bool
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
fmt.Println(optDefault.IsNil())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="IsNotNil">IsNotNil</span>
|
||||||
|
<p>检查当前Optional内是否存在值。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) IsNotNil() bool
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var value *int = nil
|
||||||
|
opt := optional.FromNillable(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.IsNotNil())
|
||||||
|
|
||||||
|
value = new(int)
|
||||||
|
*value = 42
|
||||||
|
opt = optional.FromNillable(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.IsNotNil())
|
||||||
|
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="IfNotNil">IfNotNil</span>
|
||||||
|
<p>如果值存在,则使用action方法执行给定的操作。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) IfNotNil(action func(value T))
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
called := false
|
||||||
|
action := func(value int) { called = true }
|
||||||
|
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
optDefault.IfNotNil(action)
|
||||||
|
|
||||||
|
fmt.Println(called)
|
||||||
|
|
||||||
|
called = false // Reset for next test
|
||||||
|
optWithValue := optional.Of(42)
|
||||||
|
optWithValue.IfNotNil(action)
|
||||||
|
|
||||||
|
fmt.Println(optWithValue.IsNotNil())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="IfNotNilOrElse">IfNotNilOrElse</span>
|
||||||
|
<p>根据是否存在值执行相应的操作:有值则执行指定操作,没有值则执行默认操作。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func())
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
calledWithValue := false
|
||||||
|
valueAction := func(value int) { calledWithValue = true }
|
||||||
|
emptyAction := func() { t.Errorf("Empty action should not be called when value is present") }
|
||||||
|
|
||||||
|
optWithValue := optional.Of(42)
|
||||||
|
optWithValue.IfNotNilOrElse(valueAction, emptyAction)
|
||||||
|
|
||||||
|
fmt.Println(calledWithValue)
|
||||||
|
|
||||||
|
calledWithEmpty := false
|
||||||
|
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
|
||||||
|
emptyAction = func() { calledWithEmpty = true }
|
||||||
|
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
optDefault.IfNotNilOrElse(valueAction, emptyAction)
|
||||||
|
|
||||||
|
fmt.Println(calledWithEmpty)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Unwrap">Unwrap</span>
|
||||||
|
<p>如果存在,返回该值,否则引发panic。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) Unwrap() T
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
value := 42
|
||||||
|
opt := optional.Of(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.Unwrap())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 42
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="OrElse">OrElse</span>
|
||||||
|
<p>检查Optional值是否存在,如果存在,则直接返回该值。如果不存在,返回参数other值。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) OrElse(other T) T
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
optDefault := optional.Empty[int]()
|
||||||
|
val := optDefault.OrElse(100)
|
||||||
|
fmt.Println(val)
|
||||||
|
|
||||||
|
optWithValue := optional.Of(42)
|
||||||
|
val = optWithValue.OrElse(100)
|
||||||
|
fmt.Println(val)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 100
|
||||||
|
// 42
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="OrElseGet">OrElseGet</span>
|
||||||
|
<p>检查Optional值是否存在,如果存在,则直接返回该值。如果不存在,则调用一个提供的函数 (supplier),并返回该函数的执行结果。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) OrElseGet(action func() T) T
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
action := func() int { return 100 }
|
||||||
|
|
||||||
|
val := optDefault.OrElseGet(action)
|
||||||
|
fmt.Println(val)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="OrElseTrigger">OrElseTrigger</span>
|
||||||
|
<p>检查Optional值是否存在,如果存在,则直接返回该值,否则返回错误。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
OrElseTrigger(errorHandler func() error) (T, error)
|
||||||
|
```
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
|
||||||
|
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
|
||||||
|
optWithValue := optional.Of(42)
|
||||||
|
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
|
||||||
|
|
||||||
|
fmt.Println(val)
|
||||||
|
fmt.Println(err)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// no value
|
||||||
|
// 42
|
||||||
|
// nil
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Set
|
# Set
|
||||||
|
|
||||||
Set 集合数据结构,类似列表。Set 中元素不重复。
|
集合数据结构,类似列表。Set中元素不重复。
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -22,8 +22,8 @@ import (
|
|||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
|
|
||||||
- [NewSet](#NewSet)
|
- [New](#New)
|
||||||
- [NewSetFromSlice](#NewSetFromSlice)
|
- [FromSlice](#FromSlice)
|
||||||
- [Values](#Values)
|
- [Values](#Values)
|
||||||
- [Add](#Add)
|
- [Add](#Add)
|
||||||
- [AddIfNotExist](#AddIfNotExist)
|
- [AddIfNotExist](#AddIfNotExist)
|
||||||
@@ -40,20 +40,23 @@ import (
|
|||||||
- [Intersection](#Intersection)
|
- [Intersection](#Intersection)
|
||||||
- [SymmetricDifference](#SymmetricDifference)
|
- [SymmetricDifference](#SymmetricDifference)
|
||||||
- [Minus](#Minus)
|
- [Minus](#Minus)
|
||||||
|
- [Pop](#Pop)
|
||||||
|
- [ToSlice](#ToSlice)
|
||||||
|
- [ToSortedSlice](#ToSortedSlice)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
|
|
||||||
### <span id="NewSet">NewSet</span>
|
### <span id="New">New</span>
|
||||||
|
|
||||||
<p>返回Set结构体对象</p>
|
<p>返回Set结构体对象</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Set[T comparable] map[T]bool
|
type Set[T comparable] map[T]struct{}
|
||||||
func NewSet[T comparable](items ...T) Set[T]
|
func New[T comparable](items ...T) Set[T]
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:</b>
|
||||||
@@ -67,19 +70,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int](1,2,2,3)
|
st := set.New[int](1,2,2,3)
|
||||||
fmt.Println(st.Values()) //1,2,3
|
fmt.Println(st.Values()) //1,2,3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="NewSetFromSlice">NewSetFromSlice</span>
|
### <span id="FromSlice">FromSlice</span>
|
||||||
|
|
||||||
<p>基于切片创建集合</p>
|
<p>基于切片创建集合</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func NewSetFromSlice[T comparable](items []T) Set[T]
|
func FromSlice[T comparable](items []T) Set[T]
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:</b>
|
||||||
@@ -93,14 +96,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
|
st := set.FromSlice([]int{1, 2, 2, 3})
|
||||||
fmt.Println(st.Values()) //1,2,3
|
fmt.Println(st.Values()) //1,2,3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="Values">Values</span>
|
### <span id="Values">Values<sup>deprecated</sup></span>
|
||||||
|
|
||||||
<p>获取集合中所有元素的切片</p>
|
<p>获取集合中所有元素的切片<br>
|
||||||
|
<a href='#ToSlice'>ToSlice()</a> 方法提供与 Values 方法相同的功能</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -119,7 +123,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int](1,2,2,3)
|
st := set.New[int](1,2,2,3)
|
||||||
fmt.Println(st.Values()) //1,2,3
|
fmt.Println(st.Values()) //1,2,3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -145,7 +149,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int]()
|
st := set.New[int]()
|
||||||
st.Add(1, 2, 3)
|
st.Add(1, 2, 3)
|
||||||
|
|
||||||
fmt.Println(st.Values()) //1,2,3
|
fmt.Println(st.Values()) //1,2,3
|
||||||
@@ -173,7 +177,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int]()
|
st := set.New[int]()
|
||||||
st.Add(1, 2, 3)
|
st.Add(1, 2, 3)
|
||||||
|
|
||||||
r1 := st.AddIfNotExist(1)
|
r1 := st.AddIfNotExist(1)
|
||||||
@@ -206,7 +210,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int]()
|
st := set.New[int]()
|
||||||
st.Add(1, 2)
|
st.Add(1, 2)
|
||||||
|
|
||||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||||
@@ -245,7 +249,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int]()
|
st := set.New[int]()
|
||||||
st.Add(1, 2, 3)
|
st.Add(1, 2, 3)
|
||||||
|
|
||||||
set.Delete(3)
|
set.Delete(3)
|
||||||
@@ -274,7 +278,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int]()
|
st := set.New[int]()
|
||||||
st.Add(1, 2, 3)
|
st.Add(1, 2, 3)
|
||||||
|
|
||||||
fmt.Println(st.Contain(1)) //true
|
fmt.Println(st.Contain(1)) //true
|
||||||
@@ -303,9 +307,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(1, 2)
|
set2 := set.New(1, 2)
|
||||||
set3 := set.NewSet(1, 2, 3, 4)
|
set3 := set.New(1, 2, 3, 4)
|
||||||
|
|
||||||
fmt.Println(set1.ContainAll(set2)) //true
|
fmt.Println(set1.ContainAll(set2)) //true
|
||||||
fmt.Println(set1.ContainAll(set3)) //false
|
fmt.Println(set1.ContainAll(set3)) //false
|
||||||
@@ -333,7 +337,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
|
|
||||||
fmt.Println(set1.Size()) //3
|
fmt.Println(set1.Size()) //3
|
||||||
}
|
}
|
||||||
@@ -360,7 +364,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set1.Clone()
|
set2 := set1.Clone()
|
||||||
|
|
||||||
fmt.Println(set1.Size() == set2.Size()) //true
|
fmt.Println(set1.Size() == set2.Size()) //true
|
||||||
@@ -389,9 +393,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(1, 2, 3)
|
set2 := set.New(1, 2, 3)
|
||||||
set3 := set.NewSet(1, 2, 3, 4)
|
set3 := set.New(1, 2, 3, 4)
|
||||||
|
|
||||||
fmt.Println(set1.Equal(set2)) //true
|
fmt.Println(set1.Equal(set2)) //true
|
||||||
fmt.Println(set1.Equal(set3)) //false
|
fmt.Println(set1.Equal(set3)) //false
|
||||||
@@ -419,7 +423,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
arr := []int{}
|
arr := []int{}
|
||||||
set.Iterate(func(item int) {
|
set.Iterate(func(item int) {
|
||||||
arr = append(arr, item)
|
arr = append(arr, item)
|
||||||
@@ -450,7 +454,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s := set.NewSet(1, 2, 3, 4, 5)
|
s := set.New(1, 2, 3, 4, 5)
|
||||||
|
|
||||||
var sum int
|
var sum int
|
||||||
|
|
||||||
@@ -487,8 +491,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet()
|
set2 := set.New()
|
||||||
|
|
||||||
fmt.Println(set1.IsEmpty()) //false
|
fmt.Println(set1.IsEmpty()) //false
|
||||||
fmt.Println(set2.IsEmpty()) //true
|
fmt.Println(set2.IsEmpty()) //true
|
||||||
@@ -516,8 +520,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(2, 3, 4, 5)
|
set2 := set.New(2, 3, 4, 5)
|
||||||
set3 := set1.Union(set2)
|
set3 := set1.Union(set2)
|
||||||
|
|
||||||
fmt.Println(set3.Values()) //1,2,3,4,5
|
fmt.Println(set3.Values()) //1,2,3,4,5
|
||||||
@@ -545,8 +549,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(2, 3, 4, 5)
|
set2 := set.New(2, 3, 4, 5)
|
||||||
set3 := set1.Intersection(set2)
|
set3 := set1.Intersection(set2)
|
||||||
|
|
||||||
fmt.Println(set3.Values()) //2,3
|
fmt.Println(set3.Values()) //2,3
|
||||||
@@ -574,8 +578,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(2, 3, 4, 5)
|
set2 := set.New(2, 3, 4, 5)
|
||||||
set3 := set1.SymmetricDifference(set2)
|
set3 := set1.SymmetricDifference(set2)
|
||||||
|
|
||||||
fmt.Println(set3.Values()) //1,4,5
|
fmt.Println(set3.Values()) //1,4,5
|
||||||
@@ -603,9 +607,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(2, 3, 4, 5)
|
set2 := set.New(2, 3, 4, 5)
|
||||||
set3 := set.NewSet(2, 3)
|
set3 := set.New(2, 3)
|
||||||
|
|
||||||
res1 := set1.Minus(set2)
|
res1 := set1.Minus(set2)
|
||||||
fmt.Println(res1.Values()) //1
|
fmt.Println(res1.Values()) //1
|
||||||
@@ -636,7 +640,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s := set.NewSet[int]()
|
s := set.New[int]()
|
||||||
s.Add(1)
|
s.Add(1)
|
||||||
s.Add(2)
|
s.Add(2)
|
||||||
s.Add(3)
|
s.Add(3)
|
||||||
@@ -647,3 +651,58 @@ func main() {
|
|||||||
fmt.Println(ok) // true
|
fmt.Println(ok) // true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="ToSlice">ToSlice</span>
|
||||||
|
|
||||||
|
<p>以切片的形式返回集合中所有的元素(无序)</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s Set[T]) ToSlice() (v T, ok bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
s := set.New(1, 2, 3, 4, 5)
|
||||||
|
|
||||||
|
val := s.ToSlice()
|
||||||
|
fmt.Println(val) // [2 3 4 5 1]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToSortedSlice">ToSortedSlice</span>
|
||||||
|
|
||||||
|
<p>以切片的形式返回集合中所有的元素(按给定的规则排序)</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s Set[T]) ToSortedSlice() (v T, ok bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
s1 := set.New(1, 2, 3, 4, 5)
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
|
||||||
|
|
||||||
|
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
|
||||||
|
return v1 < v2
|
||||||
|
})
|
||||||
|
|
||||||
|
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
|
||||||
|
return v1.Age < v2.Age
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(res1) // [1 2 3 4 5]
|
||||||
|
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
- [CreateFile](#CreateFile)
|
- [CreateFile](#CreateFile)
|
||||||
- [CreateDir](#CreateDir)
|
- [CreateDir](#CreateDir)
|
||||||
- [CopyFile](#CopyFile)
|
- [CopyFile](#CopyFile)
|
||||||
|
- [CopyDir](#CopyDir)
|
||||||
- [CurrentPath](#CurrentPath)
|
- [CurrentPath](#CurrentPath)
|
||||||
- [FileMode](#FileMode)
|
- [FileMode](#FileMode)
|
||||||
- [MiMeType](#MiMeType)
|
- [MiMeType](#MiMeType)
|
||||||
@@ -45,9 +46,12 @@ import (
|
|||||||
- [Sha](#Sha)
|
- [Sha](#Sha)
|
||||||
- [ReadCsvFile](#ReadCsvFile)
|
- [ReadCsvFile](#ReadCsvFile)
|
||||||
- [WriteCsvFile](#WriteCsvFile)
|
- [WriteCsvFile](#WriteCsvFile)
|
||||||
|
- [WriteMapsToCsv](#WriteMapsToCsv)
|
||||||
- [WriteStringToFile](#WriteStringToFile)
|
- [WriteStringToFile](#WriteStringToFile)
|
||||||
- [WriteBytesToFile](#WriteBytesToFile)
|
- [WriteBytesToFile](#WriteBytesToFile)
|
||||||
- [ReadFile](#ReadFile)
|
- [ReadFile](#ReadFile)
|
||||||
|
- [ChunkRead](#ChunkRead)
|
||||||
|
- [ParallelChunkRead](#ParallelChunkRead)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -161,6 +165,34 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="CopyDir">CopyDir</span>
|
||||||
|
|
||||||
|
<p>拷贝文件夹到目标路径,会递归复制文件夹下所有的文件及文件夹,并且访问权限也与源文件夹保持一致。当dstPath存在时会返回error</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func CopyDir(srcPath string, dstPath string) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/YAyFTA_UuPb)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/fileutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := fileutil.CopyFile("./test_src", "./test_dest")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="CurrentPath">CurrentPath</span>
|
### <span id="CurrentPath">CurrentPath</span>
|
||||||
|
|
||||||
<p>返回当前位置的绝对路径。</p>
|
<p>返回当前位置的绝对路径。</p>
|
||||||
@@ -669,7 +701,7 @@ func main() {
|
|||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func ReadCsvFile(filepath string) ([][]string, error)
|
func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/OExTkhGEd3_u)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/OExTkhGEd3_u)</span></b>
|
||||||
@@ -701,7 +733,7 @@ func main() {
|
|||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func WriteCsvFile(filepath string, records [][]string, append bool) error
|
func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dAXm58Q5U1o)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dAXm58Q5U1o)</span></b>
|
||||||
@@ -743,6 +775,59 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="WriteMapsToCsv">WriteMapsToCsv</span>
|
||||||
|
|
||||||
|
<p>将map切片写入csv文件中。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
// filepath: CSV文件路径。
|
||||||
|
// records: 写入文件的map切片。map值必须为基本类型。会以map键的字母顺序写入。
|
||||||
|
// appendToExistingFile: 是否为追加写模式。
|
||||||
|
// delimiter: CSV文件分割符。
|
||||||
|
// headers: CSV文件表头顺序(需要与map key保持一致),不指定时按字母排序。
|
||||||
|
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/umAIomZFV1c)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/fileutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fpath := "./test.csv"
|
||||||
|
fileutil.CreateFile(fpath)
|
||||||
|
|
||||||
|
f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
records := []map[string]any{
|
||||||
|
{"Name": "Lili", "Age": "22", "Gender": "female"},
|
||||||
|
{"Name": "Jim", "Age": "21", "Gender": "male"},
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := []string{"Name", "Age", "Gender"}
|
||||||
|
err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := fileutil.ReadCsvFile(csvFilePath, ';')
|
||||||
|
|
||||||
|
fmt.Println(content)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="WriteBytesToFile">WriteBytesToFile</span>
|
### <span id="WriteBytesToFile">WriteBytesToFile</span>
|
||||||
|
|
||||||
<p>将bytes写入文件。</p>
|
<p>将bytes写入文件。</p>
|
||||||
@@ -872,9 +957,123 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(string(dat))
|
fmt.Println(string(dat))
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// User-agent: *
|
// User-agent: *
|
||||||
// Disallow: /deny
|
// Disallow: /deny
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ChunkRead">ChunkRead</span>
|
||||||
|
|
||||||
|
<p>从文件的指定偏移读取块并返回块内所有行。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/r0hPmKWhsgf)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/fileutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
const mb = 1024 * 1024
|
||||||
|
const defaultChunkSizeMB = 100
|
||||||
|
|
||||||
|
// test1.csv file content:
|
||||||
|
// Lili,22,female
|
||||||
|
// Jim,21,male
|
||||||
|
filePath := "./testdata/test1.csv" // 替换为你的文件路径
|
||||||
|
f, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var bufPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, 0, defaultChunkSizeMB*mb)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
lines, err := fileutil.ChunkRead(f, 0, 100, &bufPool)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(lines[0])
|
||||||
|
fmt.Println(lines[1])
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Lili,22,female
|
||||||
|
// Jim,21,male
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ParallelChunkRead">ParallelChunkRead</span>
|
||||||
|
|
||||||
|
<p>并行读取文件并将每个块的行发送到指定通道。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
// filePath:文件路径
|
||||||
|
// chunkSizeMB: 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整
|
||||||
|
// maxGoroutine: 并发读取分块的数量,设置为0时使用CPU核心数
|
||||||
|
// linesCh: 用于接收返回结果的通道。
|
||||||
|
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/teMXnCsdSEw)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/fileutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
const mb = 1024 * 1024
|
||||||
|
const defaultChunkSizeMB = 100 // 默认值
|
||||||
|
|
||||||
|
numParsers := runtime.NumCPU()
|
||||||
|
|
||||||
|
linesCh := make(chan []string, numParsers)
|
||||||
|
|
||||||
|
// test1.csv file content:
|
||||||
|
// Lili,22,female
|
||||||
|
// Jim,21,male
|
||||||
|
filePath := "./testdata/test1.csv"
|
||||||
|
|
||||||
|
go fileutil.ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
|
||||||
|
|
||||||
|
var totalLines int
|
||||||
|
for lines := range linesCh {
|
||||||
|
totalLines += len(lines)
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
fmt.Println(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(totalLines)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Lili,22,female
|
||||||
|
// Jim,21,male
|
||||||
|
// 2
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -7,6 +7,7 @@ function 函数包控制函数执行流程,包含部分函数式编程。
|
|||||||
## 源码:
|
## 源码:
|
||||||
|
|
||||||
- [https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go)
|
- [https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go)
|
||||||
|
- [https://github.com/duke-git/lancet/blob/main/function/predicate.go](https://github.com/duke-git/lancet/blob/main/function/predicate.go)
|
||||||
- [https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go)
|
- [https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
@@ -32,6 +33,14 @@ import (
|
|||||||
- [Schedule](#Schedule)
|
- [Schedule](#Schedule)
|
||||||
- [Pipeline](#Pipeline)
|
- [Pipeline](#Pipeline)
|
||||||
- [Watcher](#Watcher)
|
- [Watcher](#Watcher)
|
||||||
|
- [And](#And)
|
||||||
|
- [Or](#Or)
|
||||||
|
- [Negate](#Negate)
|
||||||
|
- [Nor](#Nor)
|
||||||
|
- [Xnor](#Xnor)
|
||||||
|
- [Nand](#Nand)
|
||||||
|
- [AcceptIf](#AcceptIf)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -404,3 +413,281 @@ func longRunningTask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="And">And</span>
|
||||||
|
|
||||||
|
<p>返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑and操作。只有当所有谓词判断函数对于给定的值都返回true时,返回true, 否则返回false。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func And[T any](predicates ...func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dTBHJMQ0zD2)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
isNumericAndLength5 := function.And(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(isNumericAndLength5("12345"))
|
||||||
|
fmt.Println(isNumericAndLength5("1234"))
|
||||||
|
fmt.Println(isNumericAndLength5("abcde"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Or">Or</span>
|
||||||
|
|
||||||
|
<p>返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑or操作。只有当所有谓词判断函数对于给定的值都返回false时,返回false, 否则返回true。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Or[T any](predicates ...func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/LitCIsDFNDA)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
containsDigitOrSpecialChar := function.Or(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(containsDigitOrSpecialChar("hello!"))
|
||||||
|
fmt.Println(containsDigitOrSpecialChar("hello"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Negate">Negate</span>
|
||||||
|
|
||||||
|
<p>返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Negate[T any](predicate func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jbI8BtgFnVE)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Define some simple predicates for demonstration
|
||||||
|
isUpperCase := func(s string) bool {
|
||||||
|
return strings.ToUpper(s) == s
|
||||||
|
}
|
||||||
|
isLowerCase := func(s string) bool {
|
||||||
|
return strings.ToLower(s) == s
|
||||||
|
}
|
||||||
|
isMixedCase := function.Negate(function.Or(isUpperCase, isLowerCase))
|
||||||
|
|
||||||
|
fmt.Println(isMixedCase("ABC"))
|
||||||
|
fmt.Println(isMixedCase("AbC"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="Nor">Nor</span>
|
||||||
|
|
||||||
|
<p>返回一个组合谓词函数,表示给定值上所有谓词逻辑非或 (nor) 的结果。只有当所有谓词函数对给定值都返回false时,该组合谓词函数才返回true。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Nor[T any](predicates ...func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/2KdCoBEOq84)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
match := function.Nor(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(match("dbcdckkeee"))
|
||||||
|
|
||||||
|
|
||||||
|
match = function.Nor(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(match("0123456789"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Nand">Nand</span>
|
||||||
|
|
||||||
|
<p>返回一个复合谓词函数,表示给定谓词函数列表的逻辑非与 (NAND)。仅当列表中所有函数对给定参数返回false时,才返回true,否则返回false。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Nand[T any](predicates ...func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Rb-FdNGpgSO)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
isNumericAndLength5 := function.Nand(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(isNumericAndLength5("12345"))
|
||||||
|
fmt.Println(isNumericAndLength5("1234"))
|
||||||
|
fmt.Println(isNumericAndLength5("abcdef"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Xnor">Xnor</span>
|
||||||
|
|
||||||
|
<p>返回一个复合谓词函数,表示给定一组谓词函数的逻辑异或 (XNOR)。只有当所有 谓词函数对给参数都返回true或false时,该谓词函数才返回true。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Xnor[T any](predicates ...func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/FJxko8SFbqc)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
isEven := func(i int) bool { return i%2 == 0 }
|
||||||
|
isPositive := func(i int) bool { return i > 0 }
|
||||||
|
|
||||||
|
match := function.Xnor(isEven, isPositive)
|
||||||
|
|
||||||
|
fmt.Println(match(2))
|
||||||
|
fmt.Println(match(-3))
|
||||||
|
fmt.Println(match(3))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="AcceptIf">AcceptIf</span>
|
||||||
|
|
||||||
|
<p>AcceptIf函数会返回另一个函数,该函数的签名与 apply 函数相同,但同时还会包含一个布尔值来表示成功或失败。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/XlXHHtzCf7d)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
adder := function.AcceptIf(
|
||||||
|
function.And(
|
||||||
|
func(x int) bool {
|
||||||
|
return x > 10
|
||||||
|
}, func(x int) bool {
|
||||||
|
return x%2 == 0
|
||||||
|
}),
|
||||||
|
func(x int) int {
|
||||||
|
return x + 1
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
result, ok := adder(20)
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(ok)
|
||||||
|
|
||||||
|
result, ok = adder(21)
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(ok)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 21
|
||||||
|
// true
|
||||||
|
// 0
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
@@ -44,6 +44,9 @@ import (
|
|||||||
- [Minus](#Minus)
|
- [Minus](#Minus)
|
||||||
- [IsDisjoint](#IsDisjoint)
|
- [IsDisjoint](#IsDisjoint)
|
||||||
- [HasKey](#HasKey)
|
- [HasKey](#HasKey)
|
||||||
|
- [MapToStruct](#MapToStruct)
|
||||||
|
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
|
||||||
|
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
|
||||||
- [NewConcurrentMap](#NewConcurrentMap)
|
- [NewConcurrentMap](#NewConcurrentMap)
|
||||||
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
||||||
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
||||||
@@ -52,6 +55,8 @@ import (
|
|||||||
- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete)
|
- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete)
|
||||||
- [ConcurrentMap_Has](#ConcurrentMap_Has)
|
- [ConcurrentMap_Has](#ConcurrentMap_Has)
|
||||||
- [ConcurrentMap_Range](#ConcurrentMap_Range)
|
- [ConcurrentMap_Range](#ConcurrentMap_Range)
|
||||||
|
- [GetOrSet](#GetOrSet)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -946,14 +951,6 @@ func main() {
|
|||||||
|
|
||||||
<p>检查map是否包含某个key。用于代替以下样板代码:</p>
|
<p>检查map是否包含某个key。用于代替以下样板代码:</p>
|
||||||
|
|
||||||
```go
|
|
||||||
_, haskey := amap["baz"];
|
|
||||||
|
|
||||||
if haskey {
|
|
||||||
fmt.Println("map has key baz")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -988,6 +985,141 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="MapToStruct">MapToStruct</span>
|
||||||
|
|
||||||
|
<p>将map转成struct。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func MapToStruct(m map[string]any, structObj any) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7wYyVfX38Dp)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
personReqMap := map[string]any{
|
||||||
|
"name": "Nothin",
|
||||||
|
"max_age": 35,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersonReq struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MaxAge int `json:"max_age"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
|
}
|
||||||
|
var personReq PersonReq
|
||||||
|
_ = maputil.MapToStruct(personReqMap, &personReq)
|
||||||
|
fmt.Println(personReq)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {Nothin 35 1 10}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToSortedSlicesDefault">ToSortedSlicesDefault</span>
|
||||||
|
|
||||||
|
<p>将map的key和value转化成两个根据key的值从小到大排序的切片,value切片中元素的位置与key对应。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/43gEM2po-qy)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, values := ToSortedSlicesDefault(m)
|
||||||
|
|
||||||
|
fmt.Println(keys)
|
||||||
|
fmt.Println(values)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2 3]
|
||||||
|
// [a b c]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToSortedSlicesWithComparator">ToSortedSlicesWithComparator</span>
|
||||||
|
|
||||||
|
<p>将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片,value切片中元素的位置与key对应。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/0nlPo6YLdt3)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m1 := map[time.Time]string{
|
||||||
|
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
|
||||||
|
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
|
||||||
|
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys1, values1 := maputil.ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
|
||||||
|
return a.Before(b)
|
||||||
|
})
|
||||||
|
|
||||||
|
m2 := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
keys2, values2 := maputil.ToSortedSlicesWithComparator(m2, func(a, b int) bool {
|
||||||
|
return a > b
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(keys2)
|
||||||
|
fmt.Println(values2)
|
||||||
|
|
||||||
|
fmt.Println(keys1)
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="NewConcurrentMap">NewConcurrentMap</span>
|
### <span id="NewConcurrentMap">NewConcurrentMap</span>
|
||||||
|
|
||||||
<p>ConcurrentMap协程安全的map结构。</p>
|
<p>ConcurrentMap协程安全的map结构。</p>
|
||||||
@@ -1050,15 +1182,15 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
||||||
fmt.Println(val, ok)
|
fmt.Println(val, ok)
|
||||||
wg2.Done()
|
wg2.Done()
|
||||||
}(j)
|
}(j)
|
||||||
}
|
}
|
||||||
wg2.Wait()
|
wg2.Wait()
|
||||||
|
|
||||||
// output: (order may change)
|
// output: (order may change)
|
||||||
// 1 true
|
// 1 true
|
||||||
@@ -1104,15 +1236,15 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
||||||
fmt.Println(val, ok)
|
fmt.Println(val, ok)
|
||||||
wg2.Done()
|
wg2.Done()
|
||||||
}(j)
|
}(j)
|
||||||
}
|
}
|
||||||
wg2.Wait()
|
wg2.Wait()
|
||||||
|
|
||||||
// output: (order may change)
|
// output: (order may change)
|
||||||
// 1 true
|
// 1 true
|
||||||
@@ -1202,7 +1334,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
cm.Delete(fmt.Sprintf("%d", n))
|
cm.Delete(fmt.Sprintf("%d", n))
|
||||||
@@ -1248,7 +1380,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n))
|
val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n))
|
||||||
@@ -1298,7 +1430,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
|
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
@@ -1353,3 +1485,41 @@ func main() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="GetOrSet">GetOrSet</span>
|
||||||
|
|
||||||
|
<p>返回给定键的值,如果不存在则设置该值。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
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>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
}
|
||||||
|
|
||||||
|
result1 := maputil.GetOrSet(m, 1, "1")
|
||||||
|
result2 := maputil.GetOrSet(m, 2, "b")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// a
|
||||||
|
// b
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -34,6 +34,10 @@ import (
|
|||||||
- [RoundToFloat](#RoundToFloat)
|
- [RoundToFloat](#RoundToFloat)
|
||||||
- [RoundToString](#RoundToString)
|
- [RoundToString](#RoundToString)
|
||||||
- [TruncRound](#TruncRound)
|
- [TruncRound](#TruncRound)
|
||||||
|
- [CeilToFloat](#CeilToFloat)
|
||||||
|
- [CeilToString](#CeilToString)
|
||||||
|
- [FloorToFloat](#FloorToFloat)
|
||||||
|
- [FloorToString](#FloorToString)
|
||||||
- [Range](#Range)
|
- [Range](#Range)
|
||||||
- [RangeWithStep](#RangeWithStep)
|
- [RangeWithStep](#RangeWithStep)
|
||||||
- [AngleToRadian](#AngleToRadian)
|
- [AngleToRadian](#AngleToRadian)
|
||||||
@@ -47,6 +51,7 @@ import (
|
|||||||
- [Log](#Log)
|
- [Log](#Log)
|
||||||
- [Sum](#Sum)
|
- [Sum](#Sum)
|
||||||
- [Abs](#Abs)
|
- [Abs](#Abs)
|
||||||
|
- [Div](#Div)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -392,7 +397,7 @@ func main() {
|
|||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func RoundToFloat(x float64, n int) float64
|
func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ghyb528JRJL)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ghyb528JRJL)</span></b>
|
||||||
@@ -428,7 +433,7 @@ func main() {
|
|||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func RoundToString(x float64, n int) string
|
func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/kZwpBRAcllO)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/kZwpBRAcllO)</span></b>
|
||||||
@@ -464,7 +469,7 @@ func main() {
|
|||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func TruncRound(x float64, n int) float64
|
func TruncRound[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>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/aumarSHIGzP)</span></b>
|
||||||
@@ -493,6 +498,150 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="CeilToFloat">CeilToFloat</span>
|
||||||
|
|
||||||
|
<p>向上舍入(进一法),保留n位小数。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/8hOeSADZPCo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result1 := mathutil.CeilToFloat(3.14159, 1)
|
||||||
|
result2 := mathutil.CeilToFloat(3.14159, 2)
|
||||||
|
result3 := mathutil.CeilToFloat(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.2
|
||||||
|
// 3.15
|
||||||
|
// 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="CeilToString">CeilToString</span>
|
||||||
|
|
||||||
|
<p>向上舍入(进一法),保留n位小数,返回字符串。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wy5bYEyUKKG)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result1 := mathutil.CeilToString(3.14159, 1)
|
||||||
|
result2 := mathutil.CeilToString(3.14159, 2)
|
||||||
|
result3 := mathutil.CeilToString(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.2
|
||||||
|
// 3.15
|
||||||
|
// 5.0000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="FloorToFloat">FloorToFloat</span>
|
||||||
|
|
||||||
|
<p>向下舍入(去尾法),保留n位小数。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vbCBrQHZEED)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result1 := mathutil.FloorToFloat(3.14159, 1)
|
||||||
|
result2 := mathutil.FloorToFloat(3.14159, 2)
|
||||||
|
result3 := mathutil.FloorToFloat(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.1
|
||||||
|
// 3.14
|
||||||
|
// 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="FloorToString">FloorToString</span>
|
||||||
|
|
||||||
|
<p>向下舍入(去尾法),保留n位小数,返回字符串。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Qk9KPd2IdDb)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result1 := mathutil.FloorToString(3.14159, 1)
|
||||||
|
result2 := mathutil.FloorToString(3.14159, 2)
|
||||||
|
result3 := mathutil.FloorToString(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.1
|
||||||
|
// 3.14
|
||||||
|
// 5.0000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="Range">Range</span>
|
### <span id="Range">Range</span>
|
||||||
|
|
||||||
<p>根据指定的起始值和数量,创建一个数字切片。</p>
|
<p>根据指定的起始值和数量,创建一个数字切片。</p>
|
||||||
@@ -965,16 +1114,52 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
result1 := Abs(-1)
|
result1 := Abs(-1)
|
||||||
result2 := Abs(-0.1)
|
result2 := Abs(-0.1)
|
||||||
result3 := Abs(float32(0.2))
|
result3 := Abs(float32(0.2))
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
fmt.Println(result3)
|
fmt.Println(result3)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// 1
|
// 1
|
||||||
// 0.1
|
// 0.1
|
||||||
// 0.2
|
// 0.2
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="Div">Div</span>
|
||||||
|
|
||||||
|
<p>除法运算。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/WLxDdGXXYat)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result1 := mathutil.Div(9, 4)
|
||||||
|
result2 := mathutil.Div(1, 2)
|
||||||
|
result3 := mathutil.Div(0, 666)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 2.25
|
||||||
|
// 0.5
|
||||||
|
// 0
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -25,8 +25,8 @@ import (
|
|||||||
- [Of](#Of)
|
- [Of](#Of)
|
||||||
- [Unwrap](#Unwrap)
|
- [Unwrap](#Unwrap)
|
||||||
- [ExtractPointer](#ExtractPointer)
|
- [ExtractPointer](#ExtractPointer)
|
||||||
- [UnwarpOr](#UnwarpOr)
|
- [UnwrapOr](#UnwrapOr)
|
||||||
- [UnwarpOrDefault](#UnwarpOrDefault)
|
- [UnwrapOrDefault](#UnwrapOrDefault)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -136,14 +136,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="UnwarpOr">UnwarpOr</span>
|
### <span id="UnwrapOr">UnwrapOr</span>
|
||||||
|
|
||||||
<p>返回指针的值,如果指针为零值,则返回fallback。</p>
|
<p>返回指针的值,如果指针为零值,则返回fallback。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
UnwarpOr[T any](p *T, fallback T) T
|
UnwrapOr[T any](p *T, fallback T) T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/mmNaLC38W8C)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/mmNaLC38W8C)</span></b>
|
||||||
@@ -163,10 +163,10 @@ func main() {
|
|||||||
var c *int
|
var c *int
|
||||||
var d *string
|
var d *string
|
||||||
|
|
||||||
result1 := pointer.UnwarpOr(&a, 456)
|
result1 := pointer.UnwrapOr(&a, 456)
|
||||||
result2 := pointer.UnwarpOr(&b, "abc")
|
result2 := pointer.UnwrapOr(&b, "abc")
|
||||||
result3 := pointer.UnwarpOr(c, 456)
|
result3 := pointer.UnwrapOr(c, 456)
|
||||||
result4 := pointer.UnwarpOr(d, "def")
|
result4 := pointer.UnwrapOr(d, "def")
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
@@ -181,14 +181,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="UnwarpOrDefault">UnwarpOrDefault</span>
|
### <span id="UnwrapOrDefault">UnwrapOrDefault</span>
|
||||||
|
|
||||||
<p>返回指针的值,如果指针为零值,则返回相应零值。</p>
|
<p>返回指针的值,如果指针为零值,则返回相应零值。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
UnwarpOrDefault[T any](p *T) T
|
UnwrapOrDefault[T any](p *T) T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
|
||||||
@@ -208,10 +208,10 @@ func main() {
|
|||||||
var c *int
|
var c *int
|
||||||
var d *string
|
var d *string
|
||||||
|
|
||||||
result1 := pointer.UnwarpOrDefault(&a)
|
result1 := pointer.UnwrapOrDefault(&a)
|
||||||
result2 := pointer.UnwarpOrDefault(&b)
|
result2 := pointer.UnwrapOrDefault(&b)
|
||||||
result3 := pointer.UnwarpOrDefault(c)
|
result3 := pointer.UnwrapOrDefault(c)
|
||||||
result4 := pointer.UnwarpOrDefault(d)
|
result4 := pointer.UnwrapOrDefault(d)
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ import (
|
|||||||
- [RetryFunc](#RetryFunc)
|
- [RetryFunc](#RetryFunc)
|
||||||
- [RetryDuration](#RetryDuration)
|
- [RetryDuration](#RetryDuration)
|
||||||
- [RetryTimes](#RetryTimes)
|
- [RetryTimes](#RetryTimes)
|
||||||
|
- [BackoffStrategy](#BackoffStrategy)
|
||||||
|
- [RetryWithCustomBackoff](#RetryWithCustomBackoff)
|
||||||
|
- [RetryWithLinearBackoff](#RetryWithLinearBackoff)
|
||||||
|
- [RetryWithExponentialWithJitterBackoff](#RetryWithExponentialWithJitterBackoff)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -260,3 +264,201 @@ func main() {
|
|||||||
// 3
|
// 3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="BackoffStrategy">BackoffStrategy</span>
|
||||||
|
|
||||||
|
<p>定义计算退避间隔的方法的接口。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
// BackoffStrategy is an interface that defines a method for calculating backoff intervals.
|
||||||
|
type BackoffStrategy interface {
|
||||||
|
// CalculateInterval returns the time.Duration after which the next retry attempt should be made.
|
||||||
|
CalculateInterval() time.Duration
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"github.com/duke-git/lancet/v2/retry"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExampleCustomBackoffStrategy struct {
|
||||||
|
interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
|
||||||
|
return c.interval + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
number := 0
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
if number == 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&示例CustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(number)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RetryWithCustomBackoff">RetryWithCustomBackoff</span>
|
||||||
|
|
||||||
|
<p>设置自定义退避策略。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"github.com/duke-git/lancet/v2/retry"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExampleCustomBackoffStrategy struct {
|
||||||
|
interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
|
||||||
|
return c.interval + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
number := 0
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
if number == 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&示例CustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(number)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="RetryWithLinearBackoff">RetryWithLinearBackoff</span>
|
||||||
|
|
||||||
|
<p>设置线性策略退避。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RetryWithLinearBackoff(interval time.Duration) Option
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</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")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := retry.Retry(increaseNumber, retry.RetryWithLinearBackoff(time.Microsecond*50))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(number)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="RetryWithExponentialWithJitterBackoff">RetryWithExponentialWithJitterBackoff</span>
|
||||||
|
|
||||||
|
<p>设置指数策略退避。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</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")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := retry.Retry(increaseNumber, retry.RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(number)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import (
|
|||||||
- [DifferenceBy](#DifferenceBy)
|
- [DifferenceBy](#DifferenceBy)
|
||||||
- [DifferenceWith](#DifferenceWith)
|
- [DifferenceWith](#DifferenceWith)
|
||||||
- [DeleteAt](#DeleteAt)
|
- [DeleteAt](#DeleteAt)
|
||||||
|
- [DeleteRange](#DeleteRange)
|
||||||
- [Drop](#Drop)
|
- [Drop](#Drop)
|
||||||
- [DropRight](#DropRight)
|
- [DropRight](#DropRight)
|
||||||
- [DropWhile](#DropWhile)
|
- [DropWhile](#DropWhile)
|
||||||
@@ -85,6 +86,7 @@ import (
|
|||||||
- [ToSlicePointer](#ToSlicePointer)
|
- [ToSlicePointer](#ToSlicePointer)
|
||||||
- [Unique](#Unique)
|
- [Unique](#Unique)
|
||||||
- [UniqueBy](#UniqueBy)
|
- [UniqueBy](#UniqueBy)
|
||||||
|
- [UniqueByField](#UniqueByField)
|
||||||
- [Union](#Union)
|
- [Union](#Union)
|
||||||
- [UnionBy](#UnionBy)
|
- [UnionBy](#UnionBy)
|
||||||
- [UpdateAt](#UpdateAt)
|
- [UpdateAt](#UpdateAt)
|
||||||
@@ -92,6 +94,10 @@ import (
|
|||||||
- [KeyBy](#KeyBy)
|
- [KeyBy](#KeyBy)
|
||||||
- [Join](#Join)
|
- [Join](#Join)
|
||||||
- [Partition](#Partition)
|
- [Partition](#Partition)
|
||||||
|
- [SetToDefaultIf](#SetToDefaultIf)
|
||||||
|
- [Break](#Break)
|
||||||
|
- [RightPadding](#RightPadding)
|
||||||
|
- [LeftPadding](#LeftPadding)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -317,12 +323,12 @@ func main() {
|
|||||||
|
|
||||||
### <span id="Concat">Concat</span>
|
### <span id="Concat">Concat</span>
|
||||||
|
|
||||||
<p>合并多个slices到slice中</p>
|
<p>创建一个新的切片,将传入的切片拼接起来返回。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func Concat[T any](slice []T, slices ...[]T) []T
|
func Concat[T any](slices ...[]T) []T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
|
||||||
@@ -516,15 +522,15 @@ func main() {
|
|||||||
|
|
||||||
### <span id="DeleteAt">DeleteAt</span>
|
### <span id="DeleteAt">DeleteAt</span>
|
||||||
|
|
||||||
<p>删除切片中指定开始索引到结束索引的元素</p>
|
<p>删除切片中指定索引的元素(不修改原切片)。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func DeleteAt[T any](slice []T, start int, end ...int)
|
func DeleteAt[T any](slice []T, index int) []T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/pJ-d6MUWcvK)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/800B1dPBYyd)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
@@ -533,18 +539,66 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
result1 := slice.DeleteAt([]string{"a", "b", "c"}, -1)
|
chars := []string{"a", "b", "c", "d", "e"}
|
||||||
result2 := slice.DeleteAt([]string{"a", "b", "c"}, 0)
|
|
||||||
result3 := slice.DeleteAt([]string{"a", "b", "c"}, 0, 2)
|
result1 := slice.DeleteAt(chars, 0)
|
||||||
|
result2 := slice.DeleteAt(chars, 4)
|
||||||
|
result3 := slice.DeleteAt(chars, 5)
|
||||||
|
result4 := slice.DeleteAt(chars, 6)
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
fmt.Println(result3)
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [a b c]
|
// [b c d e]
|
||||||
// [b c]
|
// [a b c d]
|
||||||
// [c]
|
// [a b c d]
|
||||||
|
// [a b c d]
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="DeleteRange">DeleteRange</span>
|
||||||
|
|
||||||
|
<p>删除切片中指定索引范围的元素(不修改原切片)。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func DeleteRange[T any](slice []T, start, end int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/945HwiNrnle)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
chars := []string{"a", "b", "c", "d", "e"}
|
||||||
|
|
||||||
|
result1 := DeleteRange(chars, 0, 0)
|
||||||
|
result2 := DeleteRange(chars, 0, 1)
|
||||||
|
result3 := DeleteRange(chars, 0, 3)
|
||||||
|
result4 := DeleteRange(chars, 0, 4)
|
||||||
|
result5 := DeleteRange(chars, 0, 5)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
fmt.Println(result5)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [a b c d e]
|
||||||
|
// [b c d e]
|
||||||
|
// [d e]
|
||||||
|
// [e]
|
||||||
|
// []
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -1489,7 +1543,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="Merge">Merge</span>
|
### <span id="Merge">Merge(废弃:使用Concat)</span>
|
||||||
|
|
||||||
<p>合并多个切片(不会消除重复元素).</p>
|
<p>合并多个切片(不会消除重复元素).</p>
|
||||||
|
|
||||||
@@ -2259,6 +2313,47 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="UniqueByField">UniqueByField</span>
|
||||||
|
|
||||||
|
<p>根据struct字段对struct切片去重复。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func UniqueByField[T any](slice []T, field string) ([]T, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;"></span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []User{
|
||||||
|
{ID: 1, Name: "a"},
|
||||||
|
{ID: 2, Name: "b"},
|
||||||
|
{ID: 1, Name: "c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := slice.UniqueByField(users, "ID")
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [{1 a} {2 b}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="Union">Union</span>
|
### <span id="Union">Union</span>
|
||||||
|
|
||||||
<p>合并多个切片</p>
|
<p>合并多个切片</p>
|
||||||
@@ -2475,18 +2570,18 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
nums := []int{1, 2, 3, 4, 5}
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
result1 := slice.Partition(nums)
|
result1 := slice.Partition(nums)
|
||||||
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
|
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
|
||||||
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
|
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
fmt.Println(result3)
|
fmt.Println(result3)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [[1 2 3 4 5]]
|
// [[1 2 3 4 5]]
|
||||||
// [[2 4] [1 3 5]]
|
// [[2 4] [1 3 5]]
|
||||||
// [[1 2] [3 4] [5]]
|
// [[1 2] [3 4] [5]]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -2510,13 +2605,131 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
nums := []int{1, 2, 3, 4, 5}
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
val, idx := slice.Random(nums)
|
val, idx := slice.Random(nums)
|
||||||
if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) {
|
if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) {
|
||||||
fmt.Println("okk")
|
fmt.Println("okk")
|
||||||
}
|
}
|
||||||
// Output:
|
// Output:
|
||||||
// okk
|
// okk
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="SetToDefaultIf">SetToDefaultIf</span>
|
||||||
|
|
||||||
|
<p>根据给定给定的predicate判定函数来修改切片中的元素。对于满足的元素,将其替换为指定的默认值,同时保持元素在切片中的位置不变。函数返回修改后的切片以及被修改的元素个数。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9AXGlPRC0-A)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||||
|
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||||
|
|
||||||
|
fmt.Println(modifiedStrs)
|
||||||
|
fmt.Println(count)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [ b c d ]
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Break">Break</span>
|
||||||
|
|
||||||
|
<p>根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Break[T any](values []T, predicate func(T) bool) ([]T, []T)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/yLYcBTyeQIz)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
even := func(n int) bool { return n%2 == 0 }
|
||||||
|
|
||||||
|
resultEven, resultAfterFirstEven := slice.Break(nums, even)
|
||||||
|
|
||||||
|
fmt.Println(resultEven)
|
||||||
|
fmt.Println(resultAfterFirstEven)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1]
|
||||||
|
// [2 3 4 5]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<span id="RightPadding">RightPadding</span>
|
||||||
|
|
||||||
|
<p>在切片的右部添加元素。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/0_2rlLEMBXL)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<span id="LeftPadding">LeftPadding</span>
|
||||||
|
|
||||||
|
<p>在切片的左部添加元素。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jlQVoelLl2k)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
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]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -60,6 +60,9 @@ import (
|
|||||||
- [ContainsAll](#ContainsAll)
|
- [ContainsAll](#ContainsAll)
|
||||||
- [ContainsAny](#ContainsAny)
|
- [ContainsAny](#ContainsAny)
|
||||||
- [RemoveWhiteSpace](#RemoveWhiteSpace)
|
- [RemoveWhiteSpace](#RemoveWhiteSpace)
|
||||||
|
- [SubInBetween](#SubInBetween)
|
||||||
|
- [HammingDistance](#HammingDistance)
|
||||||
|
- [Concat](#Concat)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -1096,10 +1099,10 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
result1 := strutil.IsNotBlank("")
|
result1 := strutil.IsNotBlank("")
|
||||||
result2 := strutil.IsNotBlank(" ")
|
result2 := strutil.IsNotBlank(" ")
|
||||||
result3 := strutil.IsNotBlank("\t\v\f\n")
|
result3 := strutil.IsNotBlank("\t\v\f\n")
|
||||||
result4 := strutil.IsNotBlank(" 中文")
|
result4 := strutil.IsNotBlank(" 中文")
|
||||||
result5 := strutil.IsNotBlank(" world ")
|
result5 := strutil.IsNotBlank(" world ")
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
@@ -1462,3 +1465,102 @@ func main() {
|
|||||||
// hello world
|
// hello world
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="SubInBetween">SubInBetween</span>
|
||||||
|
|
||||||
|
<p>获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SubInBetween(str string, start string, end string) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/EDbaRvjeNsv)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/strutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
str := "abcde"
|
||||||
|
|
||||||
|
result1 := strutil.SubInBetween(str, "", "de")
|
||||||
|
result2 := strutil.SubInBetween(str, "a", "d")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// abc
|
||||||
|
// bc
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="HammingDistance">HammingDistance</span>
|
||||||
|
|
||||||
|
<p>计算两个字符串之间的汉明距离。汉明距离是指对应符号不同的位置数。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func HammingDistance(a, b string) (int, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/glNdQEA9HUi)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/strutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
result1, _ := strutil.HammingDistance("de", "de")
|
||||||
|
result2, _ := strutil.HammingDistance("a", "d")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 0
|
||||||
|
// 1
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
### <span id="Concat">Concat</span>
|
||||||
|
|
||||||
|
<p>拼接字符串。length是拼接后字符串的长度,如果不确定则传0或负数。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Concat(length int, str ...string) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/strutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Hello World!
|
||||||
|
// Go Language
|
||||||
|
// An apple a day,keeps the doctor away
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -43,6 +43,10 @@ import (
|
|||||||
- [ToInterface](#ToInterface)
|
- [ToInterface](#ToInterface)
|
||||||
- [Utf8ToGbk](#Utf8ToGbk)
|
- [Utf8ToGbk](#Utf8ToGbk)
|
||||||
- [GbkToUtf8](#GbkToUtf8)
|
- [GbkToUtf8](#GbkToUtf8)
|
||||||
|
- [ToStdBase64](#ToStdBase64)
|
||||||
|
- [ToUrlBase64](#ToUrlBase64)
|
||||||
|
- [ToRawStdBase64](#ToRawStdBase64)
|
||||||
|
- [ToRawUrlBase64](#ToRawUrlBase64)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -567,6 +571,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### <span id="EncodeByte">EncodeByte</span>
|
### <span id="EncodeByte">EncodeByte</span>
|
||||||
|
|
||||||
<p>Encode data to byte slice.</p>
|
<p>Encode data to byte slice.</p>
|
||||||
@@ -632,69 +637,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="DeepClone">DeepClone</span>
|
|
||||||
|
|
||||||
<p>Creates a deep copy of passed item, can't clone unexported field of struct.</p>
|
|
||||||
|
|
||||||
<b>Signature:</b>
|
|
||||||
|
|
||||||
```go
|
|
||||||
func DeepClone[T any](src T) T
|
|
||||||
```
|
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/j4DP5dquxnk)</span></b>
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/duke-git/lancet/v2/convertor"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
type Struct struct {
|
|
||||||
Str string
|
|
||||||
Int int
|
|
||||||
Float float64
|
|
||||||
Bool bool
|
|
||||||
Nil interface{}
|
|
||||||
unexported string
|
|
||||||
}
|
|
||||||
|
|
||||||
cases := []interface{}{
|
|
||||||
true,
|
|
||||||
1,
|
|
||||||
0.1,
|
|
||||||
map[string]int{
|
|
||||||
"a": 1,
|
|
||||||
"b": 2,
|
|
||||||
},
|
|
||||||
&Struct{
|
|
||||||
Str: "test",
|
|
||||||
Int: 1,
|
|
||||||
Float: 0.1,
|
|
||||||
Bool: true,
|
|
||||||
Nil: nil,
|
|
||||||
// unexported: "can't be cloned",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, item := range cases {
|
|
||||||
cloned := convertor.DeepClone(item)
|
|
||||||
|
|
||||||
isPointerEqual := &cloned == &item
|
|
||||||
fmt.Println(cloned, isPointerEqual)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// true false
|
|
||||||
// 1 false
|
|
||||||
// 0.1 false
|
|
||||||
// map[a:1 b:2] false
|
|
||||||
// &{test 1 0.1 true <nil> } false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### <span id="CopyProperties">CopyProperties</span>
|
### <span id="CopyProperties">CopyProperties</span>
|
||||||
|
|
||||||
@@ -775,41 +717,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="ToInterface">ToInterface</span>
|
|
||||||
|
|
||||||
<p>Converts reflect value to its interface type.</p>
|
|
||||||
|
|
||||||
<b>Signature:</b>
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ToInterface(v reflect.Value) (value interface{}, ok bool)
|
|
||||||
```
|
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/syqw0-WG7Xd)</span></b>
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/duke-git/lancet/v2/convertor"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
val := reflect.ValueOf("abc")
|
|
||||||
iVal, ok := convertor.ToInterface(val)
|
|
||||||
|
|
||||||
fmt.Printf("%T\n", iVal)
|
|
||||||
fmt.Printf("%v\n", iVal)
|
|
||||||
fmt.Println(ok)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// string
|
|
||||||
// abc
|
|
||||||
// true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### <span id="Utf8ToGbk">Utf8ToGbk</span>
|
### <span id="Utf8ToGbk">Utf8ToGbk</span>
|
||||||
|
|
||||||
<p>Converts utf8 encoding data to GBK encoding data.</p>
|
<p>Converts utf8 encoding data to GBK encoding data.</p>
|
||||||
@@ -875,4 +782,338 @@ func main() {
|
|||||||
// true
|
// true
|
||||||
// hello
|
// hello
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToStdBase64">ToStdBase64</span>
|
||||||
|
|
||||||
|
<p>Convert a value to a string encoded in standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToStdBase64(value any) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/_fLJqJD3NMo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
afterEncode := convertor.ToStdBase64(nil)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
afterEncode = convertor.ToStdBase64("")
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode = convertor.ToStdBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = convertor.ToStdBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = convertor.ToStdBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = convertor.ToStdBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = convertor.ToStdBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = convertor.ToStdBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = convertor.ToStdBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// aGVsbG8=
|
||||||
|
// aGVsbG8=
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||||
|
// MTIzLjQ1Ng==
|
||||||
|
// dHJ1ZQ==
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="ToUrlBase64">ToUrlBase64</span>
|
||||||
|
|
||||||
|
<p>Convert a value to a string encoded in url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToUrlBase64(value any) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/C_d0GlvEeUR)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
afterEncode := convertor.ToUrlBase64(nil)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode = convertor.ToUrlBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = convertor.ToUrlBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = convertor.ToUrlBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = convertor.ToUrlBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = convertor.ToUrlBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = convertor.ToUrlBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = convertor.ToUrlBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
//
|
||||||
|
// aGVsbG8=
|
||||||
|
// aGVsbG8=
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||||
|
// MTIzLjQ1Ng==
|
||||||
|
// dHJ1ZQ==
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToRawStdBase64">ToRawStdBase64</span>
|
||||||
|
|
||||||
|
<p>Convert a value to a string encoded in raw standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToRawStdBase64(value any) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wSAr3sfkDcv)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode := convertor.ToRawStdBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = convertor.ToRawStdBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = convertor.ToRawStdBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = convertor.ToRawStdBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = convertor.ToRawStdBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = convertor.ToRawStdBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// aGVsbG8
|
||||||
|
// aGVsbG8
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||||
|
// MTIzLjQ1Ng
|
||||||
|
// dHJ1ZQ
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToRawUrlBase64">ToRawUrlBase64</span>
|
||||||
|
|
||||||
|
<p> Convert a value to a string encoded in raw url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON formatted string.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToRawUrlBase64(value any) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/HwdDPFcza1O)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
stringVal := "hello"
|
||||||
|
afterEncode := convertor.ToRawUrlBase64(stringVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
byteSliceVal := []byte("hello")
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
intVal := 123
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(intVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{"hello", 3}}
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(mapVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
floatVal := 123.456
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(floatVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
boolVal := true
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(boolVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
errVal := errors.New("err")
|
||||||
|
afterEncode = convertor.ToRawUrlBase64(errVal)
|
||||||
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// aGVsbG8
|
||||||
|
// aGVsbG8
|
||||||
|
// MTIz
|
||||||
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||||
|
// MTIzLjQ1Ng
|
||||||
|
// dHJ1ZQ
|
||||||
|
// ZXJy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="DeepClone">DeepClone</span>
|
||||||
|
|
||||||
|
<p>Creates a deep copy of passed item, can't clone unexported field of struct.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func DeepClone[T any](src T) T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/j4DP5dquxnk)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
type Struct struct {
|
||||||
|
Str string
|
||||||
|
Int int
|
||||||
|
Float float64
|
||||||
|
Bool bool
|
||||||
|
Nil interface{}
|
||||||
|
unexported string
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []interface{}{
|
||||||
|
true,
|
||||||
|
1,
|
||||||
|
0.1,
|
||||||
|
map[string]int{
|
||||||
|
"a": 1,
|
||||||
|
"b": 2,
|
||||||
|
},
|
||||||
|
&Struct{
|
||||||
|
Str: "test",
|
||||||
|
Int: 1,
|
||||||
|
Float: 0.1,
|
||||||
|
Bool: true,
|
||||||
|
Nil: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range cases {
|
||||||
|
cloned := convertor.DeepClone(item)
|
||||||
|
|
||||||
|
isPointerEqual := &cloned == &item
|
||||||
|
fmt.Println(cloned, isPointerEqual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true false
|
||||||
|
// 1 false
|
||||||
|
// 0.1 false
|
||||||
|
// map[a:1 b:2] false
|
||||||
|
// &{test 1 0.1 true <nil> } false
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -26,6 +26,8 @@ import (
|
|||||||
- [Remove](#Remove)
|
- [Remove](#Remove)
|
||||||
- [IndexOf](#IndexOf)
|
- [IndexOf](#IndexOf)
|
||||||
- [LastIndexOf](#LastIndexOf)
|
- [LastIndexOf](#LastIndexOf)
|
||||||
|
- [IndexOfFunc](#IndexOfFunc)
|
||||||
|
- [LastIndexOfFunc](#LastIndexOfFunc)
|
||||||
- [IsEmpty](#IsEmpty)
|
- [IsEmpty](#IsEmpty)
|
||||||
- [Contain](#Contain)
|
- [Contain](#Contain)
|
||||||
- [ValueOf](#ValueOf)
|
- [ValueOf](#ValueOf)
|
||||||
@@ -197,6 +199,59 @@ func main() {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="IndexOfFunc">IndexOfFunc</span>
|
||||||
|
<p> IndexOfFunc returns the first index satisfying the functional predicate f(v) bool. if not found return -1.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
l := list.NewCopyOnWriteList([]int{1, 2, 3})
|
||||||
|
|
||||||
|
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 1 })) //0
|
||||||
|
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 0 })) //-1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="LastIndexOfFunc">LastIndexOfFunc</span>
|
||||||
|
<p>LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying the functional predicate f(T) bool. if not found return -1.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
l := list.NewCopyOnWriteList([]int{1, 2, 3, 1})
|
||||||
|
|
||||||
|
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3
|
||||||
|
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### IsEmpty
|
### IsEmpty
|
||||||
|
|
||||||
Returns true if this list does not contain any elements.
|
Returns true if this list does not contain any elements.
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import (
|
|||||||
- [Iterate](#Iterate)
|
- [Iterate](#Iterate)
|
||||||
- [Keys](#Keys)
|
- [Keys](#Keys)
|
||||||
- [Values](#Values)
|
- [Values](#Values)
|
||||||
|
- [FilterByValue](#FilterByValue)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -311,4 +312,77 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="FilterByValue">FilterByValue</span>
|
||||||
|
|
||||||
|
<p>Returns a filtered HashMap.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
hm := hashmap.NewHashMap()
|
||||||
|
|
||||||
|
hm.Put("a", 1)
|
||||||
|
hm.Put("b", 2)
|
||||||
|
hm.Put("c", 3)
|
||||||
|
hm.Put("d", 4)
|
||||||
|
hm.Put("e", 5)
|
||||||
|
hm.Put("f", 6)
|
||||||
|
|
||||||
|
filteredHM := hm.FilterByValue(func(value any) bool {
|
||||||
|
return value.(int) == 1 || value.(int) == 3
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(filteredHM.Size()) //2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="ToInterface">ToInterface</span>
|
||||||
|
|
||||||
|
<p>Converts reflect value to its interface type.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToInterface(v reflect.Value) (value interface{}, ok bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/syqw0-WG7Xd)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
val := reflect.ValueOf("abc")
|
||||||
|
iVal, ok := convertor.ToInterface(val)
|
||||||
|
|
||||||
|
fmt.Printf("%T\n", iVal)
|
||||||
|
fmt.Printf("%v\n", iVal)
|
||||||
|
fmt.Println(ok)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// string
|
||||||
|
// abc
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
416
docs/en/api/packages/datastructure/optional.md
Normal file
416
docs/en/api/packages/datastructure/optional.md
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
# Optional
|
||||||
|
Optional is a type that may or may not contain a non-nil value.
|
||||||
|
|
||||||
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
- [https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go](https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go)
|
||||||
|
|
||||||
|
|
||||||
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
## Index
|
||||||
|
|
||||||
|
- [Of](#Of)
|
||||||
|
- [FromNillable](#FromNillable)
|
||||||
|
- [Default](#Default)
|
||||||
|
- [IsNotNil](#IsNotNil)
|
||||||
|
- [IsNil](#IsNil)
|
||||||
|
- [IsNotNil](#IsNotNil)
|
||||||
|
- [IfNotNilOrElse](#IfNotNilOrElse)
|
||||||
|
- [Umwarp](#Umwarp)
|
||||||
|
- [OrElse](#OrElse)
|
||||||
|
- [OrElseGet](#OrElseGet)
|
||||||
|
- [OrElseTrigger](#OrElseTrigger)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
### <span id="Of">Of</span>
|
||||||
|
<p>Returns an Optional with a non-nil value.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Of[T any](value T) Optional[T]
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
value := 42
|
||||||
|
opt := optional.Of(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.Get())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 42
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="FromNillable">FromNillable</span>
|
||||||
|
<p>Returns an Optional for a given value, which may be nil.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func FromNillable[T any](value *T) Optional[T]
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var value *int = nil
|
||||||
|
opt := optional.FromNillable(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.IsNotNil())
|
||||||
|
|
||||||
|
value = new(int)
|
||||||
|
*value = 42
|
||||||
|
opt = optional.FromNillable(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.IsNotNil())
|
||||||
|
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="Default">Default</span>
|
||||||
|
<p>Returns an default Optional instance.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Default[T any]() Optional[T]
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
fmt.Println(optDefault.IsNil())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="IsNil">IsNil</span>
|
||||||
|
<p>Checks if the Optional is nil.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) IsNil() bool
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
fmt.Println(optDefault.IsNil())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="IsNotNil">IsNotNil</span>
|
||||||
|
<p>Checks if there is a value not nil.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) IsNotNil() bool
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var value *int = nil
|
||||||
|
opt := optional.FromNillable(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.IsNotNil())
|
||||||
|
|
||||||
|
value = new(int)
|
||||||
|
*value = 42
|
||||||
|
opt = optional.FromNillable(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.IsNotNil())
|
||||||
|
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="IfNotNil">IfNotNil</span>
|
||||||
|
<p>Performs the given action with the value if a value is present.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) IfNotNil(action func(value T))
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
called := false
|
||||||
|
action := func(value int) { called = true }
|
||||||
|
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
optDefault.IfNotNil(action)
|
||||||
|
|
||||||
|
fmt.Println(called)
|
||||||
|
|
||||||
|
called = false // Reset for next test
|
||||||
|
optWithValue := optional.Of(42)
|
||||||
|
optWithValue.IfNotNil(action)
|
||||||
|
|
||||||
|
fmt.Println(optWithValue.IsNotNil())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="IfNotNilOrElse">IfNotNilOrElse</span>
|
||||||
|
<p>Performs the action with the value if not nil, otherwise performs the fallback action.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func())
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
calledWithValue := false
|
||||||
|
valueAction := func(value int) { calledWithValue = true }
|
||||||
|
emptyAction := func() { t.Errorf("Empty action should not be called when value is present") }
|
||||||
|
|
||||||
|
optWithValue := optional.Of(42)
|
||||||
|
optWithValue.IfNotNilOrElse(valueAction, emptyAction)
|
||||||
|
|
||||||
|
fmt.Println(calledWithValue)
|
||||||
|
|
||||||
|
calledWithEmpty := false
|
||||||
|
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
|
||||||
|
emptyAction = func() { calledWithEmpty = true }
|
||||||
|
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
optDefault.IfNotNilOrElse(valueAction, emptyAction)
|
||||||
|
|
||||||
|
fmt.Println(calledWithEmpty)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Unwrap">Unwrap</span>
|
||||||
|
<p>Returns the value if not nil, otherwise panics.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) Unwrap() T
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
value := 42
|
||||||
|
opt := optional.Of(value)
|
||||||
|
|
||||||
|
fmt.Println(opt.Unwrap())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 42
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="OrElse">OrElse</span>
|
||||||
|
<p>Returns the value if not nill, otherwise returns other.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) OrElse(other T) T
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
val := optDefault.OrElse(100)
|
||||||
|
fmt.Println(val)
|
||||||
|
|
||||||
|
optWithValue := optional.Of(42)
|
||||||
|
val = optWithValue.OrElse(100)
|
||||||
|
fmt.Println(val)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 100
|
||||||
|
// 42
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="OrElseGet">OrElseGet</span>
|
||||||
|
<p>Returns the value if not nil, otherwise invokes action and returns the result.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o Optional[T]) OrElseGet(action func() T) T
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
action := func() int { return 100 }
|
||||||
|
|
||||||
|
val := optDefault.OrElseGet(action)
|
||||||
|
fmt.Println(val)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="OrElseTrigger">OrElseTrigger</span>
|
||||||
|
<p>Returns the value if present, otherwise returns an error.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
OrElseTrigger(errorHandler func() error) (T, error)
|
||||||
|
```
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
optDefault := optional.Default[int]()
|
||||||
|
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
|
||||||
|
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
|
||||||
|
optWithValue := optional.Of(42)
|
||||||
|
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
|
||||||
|
|
||||||
|
fmt.Println(val)
|
||||||
|
fmt.Println(err)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// no value
|
||||||
|
// 42
|
||||||
|
// nil
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -22,8 +22,8 @@ import (
|
|||||||
|
|
||||||
## Index
|
## Index
|
||||||
|
|
||||||
- [NewSet](#NewSet)
|
- [New](#New)
|
||||||
- [NewSetFromSlice](#NewSetFromSlice)
|
- [FromSlice](#FromSlice)
|
||||||
- [Values](#Values)
|
- [Values](#Values)
|
||||||
- [Add](#Add)
|
- [Add](#Add)
|
||||||
- [AddIfNotExist](#AddIfNotExist)
|
- [AddIfNotExist](#AddIfNotExist)
|
||||||
@@ -41,20 +41,23 @@ import (
|
|||||||
- [Intersection](#Intersection)
|
- [Intersection](#Intersection)
|
||||||
- [SymmetricDifference](#SymmetricDifference)
|
- [SymmetricDifference](#SymmetricDifference)
|
||||||
- [Minus](#Minus)
|
- [Minus](#Minus)
|
||||||
|
- [Pop](#Pop)
|
||||||
|
- [ToSlice](#ToSlice)
|
||||||
|
- [ToSortedSlice](#ToSortedSlice)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
### <span id="NewSet">NewSet</span>
|
### <span id="New">New</span>
|
||||||
|
|
||||||
<p>Create a set instance</p>
|
<p>Create a set instance</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Set[T comparable] map[T]bool
|
type Set[T comparable] map[T]struct{}
|
||||||
func NewSet[T comparable](items ...T) Set[T]
|
func New[T comparable](items ...T) Set[T]
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:</b>
|
||||||
@@ -68,19 +71,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int](1,2,2,3)
|
st := set.New[int](1,2,2,3)
|
||||||
fmt.Println(st.Values()) //1,2,3
|
fmt.Println(st.Values()) //1,2,3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="NewSetFromSlice">NewSetFromSlice</span>
|
### <span id="FromSlice">FromSlice</span>
|
||||||
|
|
||||||
<p>Create a set from slice</p>
|
<p>Create a set from slice</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func NewSetFromSlice[T comparable](items []T) Set[T]
|
func FromSlice[T comparable](items []T) Set[T]
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:</b>
|
||||||
@@ -94,14 +97,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
|
st := set.FromSlice([]int{1, 2, 2, 3})
|
||||||
fmt.Println(st.Values()) //1,2,3
|
fmt.Println(st.Values()) //1,2,3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="Values">Values</span>
|
### <span id="Values">Values<sup>deprecated</sup></span>
|
||||||
|
|
||||||
<p>Return slice of all set data</p>
|
<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>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -120,7 +124,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int](1,2,2,3)
|
st := set.New[int](1,2,2,3)
|
||||||
fmt.Println(st.Values()) //1,2,3
|
fmt.Println(st.Values()) //1,2,3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -146,7 +150,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int]()
|
st := set.New[int]()
|
||||||
st.Add(1, 2, 3)
|
st.Add(1, 2, 3)
|
||||||
|
|
||||||
fmt.Println(st.Values()) //1,2,3
|
fmt.Println(st.Values()) //1,2,3
|
||||||
@@ -174,7 +178,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int]()
|
st := set.New[int]()
|
||||||
st.Add(1, 2, 3)
|
st.Add(1, 2, 3)
|
||||||
|
|
||||||
r1 := st.AddIfNotExist(1)
|
r1 := st.AddIfNotExist(1)
|
||||||
@@ -207,7 +211,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int]()
|
st := set.New[int]()
|
||||||
st.Add(1, 2)
|
st.Add(1, 2)
|
||||||
|
|
||||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||||
@@ -246,7 +250,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int]()
|
st := set.New[int]()
|
||||||
st.Add(1, 2, 3)
|
st.Add(1, 2, 3)
|
||||||
|
|
||||||
set.Delete(3)
|
set.Delete(3)
|
||||||
@@ -275,7 +279,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
st := set.NewSet[int]()
|
st := set.New[int]()
|
||||||
st.Add(1, 2, 3)
|
st.Add(1, 2, 3)
|
||||||
|
|
||||||
fmt.Println(st.Contain(1)) //true
|
fmt.Println(st.Contain(1)) //true
|
||||||
@@ -304,9 +308,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(1, 2)
|
set2 := set.New(1, 2)
|
||||||
set3 := set.NewSet(1, 2, 3, 4)
|
set3 := set.New(1, 2, 3, 4)
|
||||||
|
|
||||||
fmt.Println(set1.ContainAll(set2)) //true
|
fmt.Println(set1.ContainAll(set2)) //true
|
||||||
fmt.Println(set1.ContainAll(set3)) //false
|
fmt.Println(set1.ContainAll(set3)) //false
|
||||||
@@ -334,7 +338,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
|
|
||||||
fmt.Println(set1.Size()) //3
|
fmt.Println(set1.Size()) //3
|
||||||
}
|
}
|
||||||
@@ -361,7 +365,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set1.Clone()
|
set2 := set1.Clone()
|
||||||
|
|
||||||
fmt.Println(set1.Size() == set2.Size()) //true
|
fmt.Println(set1.Size() == set2.Size()) //true
|
||||||
@@ -390,9 +394,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(1, 2, 3)
|
set2 := set.New(1, 2, 3)
|
||||||
set3 := set.NewSet(1, 2, 3, 4)
|
set3 := set.New(1, 2, 3, 4)
|
||||||
|
|
||||||
fmt.Println(set1.Equal(set2)) //true
|
fmt.Println(set1.Equal(set2)) //true
|
||||||
fmt.Println(set1.Equal(set3)) //false
|
fmt.Println(set1.Equal(set3)) //false
|
||||||
@@ -420,7 +424,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
arr := []int{}
|
arr := []int{}
|
||||||
set.Iterate(func(item int) {
|
set.Iterate(func(item int) {
|
||||||
arr = append(arr, item)
|
arr = append(arr, item)
|
||||||
@@ -451,7 +455,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s := set.NewSet(1, 2, 3, 4, 5)
|
s := set.New(1, 2, 3, 4, 5)
|
||||||
|
|
||||||
var sum int
|
var sum int
|
||||||
|
|
||||||
@@ -488,8 +492,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet()
|
set2 := set.New()
|
||||||
|
|
||||||
fmt.Println(set1.IsEmpty()) //false
|
fmt.Println(set1.IsEmpty()) //false
|
||||||
fmt.Println(set2.IsEmpty()) //true
|
fmt.Println(set2.IsEmpty()) //true
|
||||||
@@ -517,8 +521,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(2, 3, 4, 5)
|
set2 := set.New(2, 3, 4, 5)
|
||||||
set3 := set1.Union(set2)
|
set3 := set1.Union(set2)
|
||||||
|
|
||||||
fmt.Println(set3.Values()) //1,2,3,4,5
|
fmt.Println(set3.Values()) //1,2,3,4,5
|
||||||
@@ -546,8 +550,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(2, 3, 4, 5)
|
set2 := set.New(2, 3, 4, 5)
|
||||||
set3 := set1.Intersection(set2)
|
set3 := set1.Intersection(set2)
|
||||||
|
|
||||||
fmt.Println(set3.Values()) //2,3
|
fmt.Println(set3.Values()) //2,3
|
||||||
@@ -575,8 +579,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(2, 3, 4, 5)
|
set2 := set.New(2, 3, 4, 5)
|
||||||
set3 := set1.SymmetricDifference(set2)
|
set3 := set1.SymmetricDifference(set2)
|
||||||
|
|
||||||
fmt.Println(set3.Values()) //1,4,5
|
fmt.Println(set3.Values()) //1,4,5
|
||||||
@@ -604,9 +608,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
set1 := set.NewSet(1, 2, 3)
|
set1 := set.New(1, 2, 3)
|
||||||
set2 := set.NewSet(2, 3, 4, 5)
|
set2 := set.New(2, 3, 4, 5)
|
||||||
set3 := set.NewSet(2, 3)
|
set3 := set.New(2, 3)
|
||||||
|
|
||||||
res1 := set1.Minus(set2)
|
res1 := set1.Minus(set2)
|
||||||
fmt.Println(res1.Values()) //1
|
fmt.Println(res1.Values()) //1
|
||||||
@@ -637,7 +641,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s := set.NewSet[int]()
|
s := set.New[int]()
|
||||||
s.Add(1)
|
s.Add(1)
|
||||||
s.Add(2)
|
s.Add(2)
|
||||||
s.Add(3)
|
s.Add(3)
|
||||||
@@ -648,3 +652,58 @@ func main() {
|
|||||||
fmt.Println(ok) // true
|
fmt.Println(ok) // true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="ToSlice">ToSlice</span>
|
||||||
|
|
||||||
|
<p>returns a slice containing all values of the set.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s Set[T]) ToSlice() (v T, ok bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
s := set.New(1, 2, 3, 4, 5)
|
||||||
|
|
||||||
|
val := s.ToSlice()
|
||||||
|
fmt.Println(val) // [2 3 4 5 1]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToSortedSlice">ToSortedSlice</span>
|
||||||
|
|
||||||
|
<p>returns a sorted slice containing all values of the set</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (s Set[T]) ToSortedSlice() (v T, ok bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
s1 := set.New(1, 2, 3, 4, 5)
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
|
||||||
|
|
||||||
|
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
|
||||||
|
return v1 < v2
|
||||||
|
})
|
||||||
|
|
||||||
|
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
|
||||||
|
return v1.Age < v2.Age
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(res1) // [1 2 3 4 5]
|
||||||
|
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
- [CreateFile](#CreateFile)
|
- [CreateFile](#CreateFile)
|
||||||
- [CreateDir](#CreateDir)
|
- [CreateDir](#CreateDir)
|
||||||
- [CopyFile](#CopyFile)
|
- [CopyFile](#CopyFile)
|
||||||
|
- [CopyDir](#CopyDir)
|
||||||
- [CurrentPath](#CurrentPath)
|
- [CurrentPath](#CurrentPath)
|
||||||
- [FileMode](#FileMode)
|
- [FileMode](#FileMode)
|
||||||
- [MiMeType](#MiMeType)
|
- [MiMeType](#MiMeType)
|
||||||
@@ -45,9 +46,12 @@ import (
|
|||||||
- [Sha](#Sha)
|
- [Sha](#Sha)
|
||||||
- [ReadCsvFile](#ReadCsvFile)
|
- [ReadCsvFile](#ReadCsvFile)
|
||||||
- [WriteCsvFile](#WriteCsvFile)
|
- [WriteCsvFile](#WriteCsvFile)
|
||||||
|
- [WriteMapsToCsv](#WriteMapsToCsv)
|
||||||
- [WriteStringToFile](#WriteStringToFile)
|
- [WriteStringToFile](#WriteStringToFile)
|
||||||
- [WriteBytesToFile](#WriteBytesToFile)
|
- [WriteBytesToFile](#WriteBytesToFile)
|
||||||
- [ReadFile](#ReadFile)
|
- [ReadFile](#ReadFile)
|
||||||
|
- [ChunkRead](#ChunkRead)
|
||||||
|
- [ParallelChunkRead](#ParallelChunkRead)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -161,6 +165,34 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="CopyDir">CopyDir</span>
|
||||||
|
|
||||||
|
<p>Copy src directory to dst directory, it will copy all files and directories recursively. the access permission will be the same as the source directory. if dstPath exists, it will return an error.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func CopyDir(srcPath string, dstPath string) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/YAyFTA_UuPb)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/fileutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := fileutil.CopyFile("./test_src", "./test_dest")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="CurrentPath">CurrentPath</span>
|
### <span id="CurrentPath">CurrentPath</span>
|
||||||
|
|
||||||
<p>return current absolute path.</p>
|
<p>return current absolute path.</p>
|
||||||
@@ -669,7 +701,7 @@ func main() {
|
|||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func ReadCsvFile(filepath string) ([][]string, error)
|
func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/OExTkhGEd3_u)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/OExTkhGEd3_u)</span></b>
|
||||||
@@ -701,7 +733,7 @@ func main() {
|
|||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func WriteCsvFile(filepath string, records [][]string, append bool) error
|
func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/dAXm58Q5U1o)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/dAXm58Q5U1o)</span></b>
|
||||||
@@ -743,6 +775,59 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="WriteMapsToCsv">WriteMapsToCsv</span>
|
||||||
|
|
||||||
|
<p>Write slice of map to csv file.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
// filepath: path of the CSV file.
|
||||||
|
// records: slice of maps to be written. the value of map should be basic type. The maps will be sorted by key in alphabeta order, then be written into csv file.
|
||||||
|
// appendToExistingFile: If true, data will be appended to the file if it exists.
|
||||||
|
// delimiter: Delimiter to use in the CSV file.
|
||||||
|
// headers: order of the csv column headers, needs to be consistent with the key of the map.
|
||||||
|
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/umAIomZFV1c)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/fileutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fpath := "./test.csv"
|
||||||
|
fileutil.CreateFile(fpath)
|
||||||
|
|
||||||
|
f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
records := []map[string]any{
|
||||||
|
{"Name": "Lili", "Age": "22", "Gender": "female"},
|
||||||
|
{"Name": "Jim", "Age": "21", "Gender": "male"},
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := []string{"Name", "Age", "Gender"}
|
||||||
|
err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := fileutil.ReadCsvFile(csvFilePath, ';')
|
||||||
|
|
||||||
|
fmt.Println(content)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="WriteBytesToFile">WriteBytesToFile</span>
|
### <span id="WriteBytesToFile">WriteBytesToFile</span>
|
||||||
|
|
||||||
<p>Writes bytes to target file.</p>
|
<p>Writes bytes to target file.</p>
|
||||||
@@ -878,3 +963,115 @@ func main() {
|
|||||||
// Disallow: /deny
|
// Disallow: /deny
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="ChunkRead">ChunkRead</span>
|
||||||
|
|
||||||
|
<p>reads a block from the file at the specified offset and returns all lines within the block.</p>
|
||||||
|
|
||||||
|
<b>Signature :</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/r0hPmKWhsgf)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/fileutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
const mb = 1024 * 1024
|
||||||
|
const defaultChunkSizeMB = 100
|
||||||
|
|
||||||
|
// test1.csv file content:
|
||||||
|
// Lili,22,female
|
||||||
|
// Jim,21,male
|
||||||
|
filePath := "./testdata/test1.csv"
|
||||||
|
f, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var bufPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, 0, defaultChunkSizeMB*mb)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
lines, err := fileutil.ChunkRead(f, 0, 100, &bufPool)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(lines[0])
|
||||||
|
fmt.Println(lines[1])
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Lili,22,female
|
||||||
|
// Jim,21,male
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ParallelChunkRead">ParallelChunkRead</span>
|
||||||
|
|
||||||
|
<p>Reads the file in parallel and send each chunk of lines to the specified channel.</p>
|
||||||
|
|
||||||
|
<b>Signature :</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
// filePath: file path.
|
||||||
|
// chunkSizeMB: The size of the block (in MB, the default is 100MB when set to 0). Setting it too large will be detrimental. Adjust it as appropriate.
|
||||||
|
// maxGoroutine: The number of concurrent read chunks, the number of CPU cores used when set to 0.
|
||||||
|
// linesCh: The channel used to receive the returned results.
|
||||||
|
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/teMXnCsdSEw)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/fileutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
const mb = 1024 * 1024
|
||||||
|
const defaultChunkSizeMB = 100 // 默认值
|
||||||
|
|
||||||
|
numParsers := runtime.NumCPU()
|
||||||
|
|
||||||
|
linesCh := make(chan []string, numParsers)
|
||||||
|
|
||||||
|
// test1.csv file content:
|
||||||
|
// Lili,22,female
|
||||||
|
// Jim,21,male
|
||||||
|
filePath := "./testdata/test1.csv"
|
||||||
|
|
||||||
|
go fileutil.ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
|
||||||
|
|
||||||
|
var totalLines int
|
||||||
|
for lines := range linesCh {
|
||||||
|
totalLines += len(lines)
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
fmt.Println(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(totalLines)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Lili,22,female
|
||||||
|
// Jim,21,male
|
||||||
|
// 2
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -7,6 +7,7 @@ Package function can control the flow of function execution and support part of
|
|||||||
## Source:
|
## Source:
|
||||||
|
|
||||||
- [https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go)
|
- [https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go)
|
||||||
|
- [https://github.com/duke-git/lancet/blob/main/function/predicate.go](https://github.com/duke-git/lancet/blob/main/function/predicate.go)
|
||||||
- [https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go)
|
- [https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
@@ -32,6 +33,13 @@ import (
|
|||||||
- [Schedule](#Schedule)
|
- [Schedule](#Schedule)
|
||||||
- [Pipeline](#Pipeline)
|
- [Pipeline](#Pipeline)
|
||||||
- [Watcher](#Watcher)
|
- [Watcher](#Watcher)
|
||||||
|
- [And](#And)
|
||||||
|
- [Or](#Or)
|
||||||
|
- [Negate](#Negate)
|
||||||
|
- [Nor](#Nor)
|
||||||
|
- [Xnor](#Xnor)
|
||||||
|
- [Nand](#Nand)
|
||||||
|
- [AcceptIf](#AcceptIf)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -403,3 +411,283 @@ func longRunningTask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="And">And</span>
|
||||||
|
|
||||||
|
<p>Returns a composed predicate that represents the logical AND of a list of predicates. It evaluates to true only if all predicates evaluate to true for the given value.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func And[T any](predicates ...func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/dTBHJMQ0zD2)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
isNumericAndLength5 := function.And(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(isNumericAndLength5("12345"))
|
||||||
|
fmt.Println(isNumericAndLength5("1234"))
|
||||||
|
fmt.Println(isNumericAndLength5("abcde"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Or">Or</span>
|
||||||
|
|
||||||
|
<p>Returns a composed predicate that represents the logical OR of a list of predicates. It evaluates to true if at least one of the predicates evaluates to true for the given value.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Or[T any](predicates ...func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/LitCIsDFNDA)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
containsDigitOrSpecialChar := function.Or(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(containsDigitOrSpecialChar("hello!"))
|
||||||
|
fmt.Println(containsDigitOrSpecialChar("hello"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Negate">Negate</span>
|
||||||
|
|
||||||
|
<p>Returns a predicate that represents the logical negation of this predicate.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Negate[T any](predicate func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jbI8BtgFnVE)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Define some simple predicates for demonstration
|
||||||
|
isUpperCase := func(s string) bool {
|
||||||
|
return strings.ToUpper(s) == s
|
||||||
|
}
|
||||||
|
isLowerCase := func(s string) bool {
|
||||||
|
return strings.ToLower(s) == s
|
||||||
|
}
|
||||||
|
isMixedCase := function.Negate(function.Or(isUpperCase, isLowerCase))
|
||||||
|
|
||||||
|
fmt.Println(isMixedCase("ABC"))
|
||||||
|
fmt.Println(isMixedCase("AbC"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="Nor">Nor</span>
|
||||||
|
|
||||||
|
<p>Returns a composed predicate that represents the logical NOR of a list of predicates. It evaluates to true only if all predicates evaluate to false for the given value.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Nor[T any](predicates ...func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/2KdCoBEOq84)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
match := function.Nor(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(match("dbcdckkeee"))
|
||||||
|
|
||||||
|
|
||||||
|
match = function.Nor(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(match("0123456789"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Nand">Nand</span>
|
||||||
|
|
||||||
|
<p>Returns a composed predicate that represents the logical NAND of a list of predicates. It evaluates to true only if all predicates evaluate to false for the given value.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Nand[T any](predicates ...func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Rb-FdNGpgSO)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
isNumericAndLength5 := function.Nand(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(isNumericAndLength5("12345"))
|
||||||
|
fmt.Println(isNumericAndLength5("1234"))
|
||||||
|
fmt.Println(isNumericAndLength5("abcdef"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Xnor">Xnor</span>
|
||||||
|
|
||||||
|
<p>Returns a composed predicate that represents the logical XNOR of a list of predicates. It evaluates to true only if all predicates evaluate to true or false for the given value.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Xnor[T any](predicates ...func(T) bool) func(T) bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/FJxko8SFbqc)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
isEven := func(i int) bool { return i%2 == 0 }
|
||||||
|
isPositive := func(i int) bool { return i > 0 }
|
||||||
|
|
||||||
|
match := function.Xnor(isEven, isPositive)
|
||||||
|
|
||||||
|
fmt.Println(match(2))
|
||||||
|
fmt.Println(match(-3))
|
||||||
|
fmt.Println(match(3))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="AcceptIf">AcceptIf</span>
|
||||||
|
|
||||||
|
<p>AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure. A predicate function that takes an argument of type T and returns a bool. An apply function that also takes an argument of type T and returns a modified value of the same type.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/XlXHHtzCf7d)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
adder := function.AcceptIf(
|
||||||
|
function.And(
|
||||||
|
func(x int) bool {
|
||||||
|
return x > 10
|
||||||
|
}, func(x int) bool {
|
||||||
|
return x%2 == 0
|
||||||
|
}),
|
||||||
|
func(x int) int {
|
||||||
|
return x + 1
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
result, ok := adder(20)
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(ok)
|
||||||
|
|
||||||
|
result, ok = adder(21)
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(ok)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 21
|
||||||
|
// true
|
||||||
|
// 0
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
@@ -44,6 +44,9 @@ import (
|
|||||||
- [Minus](#Minus)
|
- [Minus](#Minus)
|
||||||
- [IsDisjoint](#IsDisjoint)
|
- [IsDisjoint](#IsDisjoint)
|
||||||
- [HasKey](#HasKey)
|
- [HasKey](#HasKey)
|
||||||
|
- [MapToStruct](#MapToStruct)
|
||||||
|
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
|
||||||
|
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
|
||||||
- [NewConcurrentMap](#NewConcurrentMap)
|
- [NewConcurrentMap](#NewConcurrentMap)
|
||||||
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
||||||
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
||||||
@@ -52,6 +55,7 @@ import (
|
|||||||
- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete)
|
- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete)
|
||||||
- [ConcurrentMap_Has](#ConcurrentMap_Has)
|
- [ConcurrentMap_Has](#ConcurrentMap_Has)
|
||||||
- [ConcurrentMap_Range](#ConcurrentMap_Range)
|
- [ConcurrentMap_Range](#ConcurrentMap_Range)
|
||||||
|
- [GetOrSet](#GetOrSet)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -992,6 +996,144 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="MapToStruct">MapToStruct</span>
|
||||||
|
|
||||||
|
<p>Converts map to struct</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func MapToStruct(m map[string]any, structObj any) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/7wYyVfX38Dp)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
personReqMap := map[string]any{
|
||||||
|
"name": "Nothin",
|
||||||
|
"max_age": 35,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersonReq struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MaxAge int `json:"max_age"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
|
}
|
||||||
|
var personReq PersonReq
|
||||||
|
_ = maputil.MapToStruct(personReqMap, &personReq)
|
||||||
|
fmt.Println(personReq)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {Nothin 35 1 10}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToSortedSlicesDefault">ToSortedSlicesDefault</span>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Translate the key and value of the map into two slices that are sorted in ascending order according to the key’s value, with the position of the elements in the value slice corresponding to the key.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/43gEM2po-qy)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, values := maputil.ToSortedSlicesDefault(m)
|
||||||
|
|
||||||
|
fmt.Println(keys)
|
||||||
|
fmt.Println(values)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2 3]
|
||||||
|
// [a b c]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToSortedSlicesWithComparator">ToSortedSlicesWithComparator</span>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Translate the key and value of the map into two slices that are sorted according to a custom sorting rule defined by a comparator function based on the key's value, with the position of the elements in the value slice corresponding to the key.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/0nlPo6YLdt3)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m1 := map[time.Time]string{
|
||||||
|
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
|
||||||
|
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
|
||||||
|
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys1, values1 := maputil.ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
|
||||||
|
return a.Before(b)
|
||||||
|
})
|
||||||
|
|
||||||
|
m2 := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
keys2, values2 := maputil.ToSortedSlicesWithComparator(m2, func(a, b int) bool {
|
||||||
|
return a > b
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(keys2)
|
||||||
|
fmt.Println(values2)
|
||||||
|
|
||||||
|
fmt.Println(keys1)
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="NewConcurrentMap">NewConcurrentMap</span>
|
### <span id="NewConcurrentMap">NewConcurrentMap</span>
|
||||||
|
|
||||||
<p>ConcurrentMap is like map, but is safe for concurrent use by multiple goroutines.</p>
|
<p>ConcurrentMap is like map, but is safe for concurrent use by multiple goroutines.</p>
|
||||||
@@ -1055,15 +1197,15 @@ func main() {
|
|||||||
|
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
||||||
fmt.Println(val, ok)
|
fmt.Println(val, ok)
|
||||||
wg2.Done()
|
wg2.Done()
|
||||||
}(j)
|
}(j)
|
||||||
}
|
}
|
||||||
wg2.Wait()
|
wg2.Wait()
|
||||||
|
|
||||||
// output: (order may change)
|
// output: (order may change)
|
||||||
// 1 true
|
// 1 true
|
||||||
@@ -1110,15 +1252,15 @@ func main() {
|
|||||||
|
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
||||||
fmt.Println(val, ok)
|
fmt.Println(val, ok)
|
||||||
wg2.Done()
|
wg2.Done()
|
||||||
}(j)
|
}(j)
|
||||||
}
|
}
|
||||||
wg2.Wait()
|
wg2.Wait()
|
||||||
|
|
||||||
// output: (order may change)
|
// output: (order may change)
|
||||||
// 1 true
|
// 1 true
|
||||||
@@ -1208,7 +1350,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
cm.Delete(fmt.Sprintf("%d", n))
|
cm.Delete(fmt.Sprintf("%d", n))
|
||||||
@@ -1255,7 +1397,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n))
|
val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n))
|
||||||
@@ -1307,7 +1449,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
ok := cm.Has(fmt.Sprintf("%d", n))
|
ok := cm.Has(fmt.Sprintf("%d", n))
|
||||||
@@ -1360,3 +1502,40 @@ func main() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="GetOrSet">GetOrSet</span>
|
||||||
|
|
||||||
|
<p>Returns value of the given key or set the given value value if not present.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
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>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
}
|
||||||
|
|
||||||
|
result1 := maputil.GetOrSet(m, 1, "1")
|
||||||
|
result2 := maputil.GetOrSet(m, 2, "b")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// a
|
||||||
|
// b
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -34,6 +34,10 @@ import (
|
|||||||
- [RoundToFloat](#RoundToFloat)
|
- [RoundToFloat](#RoundToFloat)
|
||||||
- [RoundToString](#RoundToString)
|
- [RoundToString](#RoundToString)
|
||||||
- [TruncRound](#TruncRound)
|
- [TruncRound](#TruncRound)
|
||||||
|
- [CeilToFloat](#CeilToFloat)
|
||||||
|
- [CeilToString](#CeilToString)
|
||||||
|
- [FloorToFloat](#FloorToFloat)
|
||||||
|
- [FloorToString](#FloorToString)
|
||||||
- [Range](#Range)
|
- [Range](#Range)
|
||||||
- [RangeWithStep](#RangeWithStep)
|
- [RangeWithStep](#RangeWithStep)
|
||||||
- [AngleToRadian](#AngleToRadian)
|
- [AngleToRadian](#AngleToRadian)
|
||||||
@@ -47,6 +51,7 @@ import (
|
|||||||
- [Log](#Log)
|
- [Log](#Log)
|
||||||
- [Sum](#Sum)
|
- [Sum](#Sum)
|
||||||
- [Abs](#Abs)
|
- [Abs](#Abs)
|
||||||
|
- [Div](#Div)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -392,7 +397,7 @@ func main() {
|
|||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func RoundToFloat(x float64, n int) float64
|
func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ghyb528JRJL)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ghyb528JRJL)</span></b>
|
||||||
@@ -428,7 +433,7 @@ func main() {
|
|||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func RoundToString(x float64, n int) string
|
func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/kZwpBRAcllO)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/kZwpBRAcllO)</span></b>
|
||||||
@@ -464,7 +469,7 @@ func main() {
|
|||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func TruncRound(x float64, n int) float64
|
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/aumarSHIGzP)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/aumarSHIGzP)</span></b>
|
||||||
@@ -493,6 +498,150 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="CeilToFloat">CeilToFloat</span>
|
||||||
|
|
||||||
|
<p>Round float up n decimal places.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/8hOeSADZPCo)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result1 := mathutil.CeilToFloat(3.14159, 1)
|
||||||
|
result2 := mathutil.CeilToFloat(3.14159, 2)
|
||||||
|
result3 := mathutil.CeilToFloat(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.2
|
||||||
|
// 3.15
|
||||||
|
// 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="CeilToString">CeilToString</span>
|
||||||
|
|
||||||
|
<p>Round float up n decimal places.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wy5bYEyUKKG)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result1 := mathutil.CeilToString(3.14159, 1)
|
||||||
|
result2 := mathutil.CeilToString(3.14159, 2)
|
||||||
|
result3 := mathutil.CeilToString(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.2
|
||||||
|
// 3.15
|
||||||
|
// 5.0000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="FloorToFloat">FloorToFloat</span>
|
||||||
|
|
||||||
|
<p>Round float down n decimal places.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/vbCBrQHZEED)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result1 := mathutil.FloorToFloat(3.14159, 1)
|
||||||
|
result2 := mathutil.FloorToFloat(3.14159, 2)
|
||||||
|
result3 := mathutil.FloorToFloat(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.1
|
||||||
|
// 3.14
|
||||||
|
// 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="FloorToString">FloorToString</span>
|
||||||
|
|
||||||
|
<p>Round float down n decimal places.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Qk9KPd2IdDb)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result1 := mathutil.FloorToString(3.14159, 1)
|
||||||
|
result2 := mathutil.FloorToString(3.14159, 2)
|
||||||
|
result3 := mathutil.FloorToString(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.1
|
||||||
|
// 3.14
|
||||||
|
// 5.0000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="Range">Range</span>
|
### <span id="Range">Range</span>
|
||||||
|
|
||||||
<p>Creates a slice of numbers from start with specified count, element step is 1.</p>
|
<p>Creates a slice of numbers from start with specified count, element step is 1.</p>
|
||||||
@@ -964,17 +1113,52 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
result1 := Abs(-1)
|
result1 := mathutil.Abs(-1)
|
||||||
result2 := Abs(-0.1)
|
result2 := mathutil.Abs(-0.1)
|
||||||
result3 := Abs(float32(0.2))
|
result3 := mathutil.Abs(float32(0.2))
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
fmt.Println(result3)
|
fmt.Println(result3)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// 1
|
// 1
|
||||||
// 0.1
|
// 0.1
|
||||||
// 0.2
|
// 0.2
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="Div">Div</span>
|
||||||
|
|
||||||
|
<p>Returns the result of x divided by y.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/WLxDdGXXYat)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result1 := mathutil.Div(9, 4)
|
||||||
|
result2 := mathutil.Div(1, 2)
|
||||||
|
result3 := mathutil.Div(0, 666)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
// Output:
|
||||||
|
// 2.25
|
||||||
|
// 0.5
|
||||||
|
// 0
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -24,8 +24,8 @@ import (
|
|||||||
|
|
||||||
- [Of](#Of)
|
- [Of](#Of)
|
||||||
- [Unwrap](#Unwrap)
|
- [Unwrap](#Unwrap)
|
||||||
- [UnwarpOr](#UnwarpOr)
|
- [UnwrapOr](#UnwrapOr)
|
||||||
- [UnwarpOrDefault](#UnwarpOrDefault)
|
- [UnwrapOrDefault](#UnwrapOrDefault)
|
||||||
- [ExtractPointer](#ExtractPointer)
|
- [ExtractPointer](#ExtractPointer)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
@@ -103,13 +103,13 @@ func main() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### <span id="UnwarpOr">UnwarpOr</span>
|
### <span id="UnwrapOr">UnwrapOr</span>
|
||||||
|
|
||||||
<p>Returns the value from the pointer or fallback if the pointer is nil.</p>
|
<p>Returns the value from the pointer or fallback if the pointer is nil.</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
```go
|
```go
|
||||||
UnwarpOr[T any](p *T, fallback T) T
|
UnwrapOr[T any](p *T, fallback T) T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/mmNaLC38W8C)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/mmNaLC38W8C)</span></b>
|
||||||
@@ -129,10 +129,10 @@ func main() {
|
|||||||
var c *int
|
var c *int
|
||||||
var d *string
|
var d *string
|
||||||
|
|
||||||
result1 := pointer.UnwarpOr(&a, 456)
|
result1 := pointer.UnwrapOr(&a, 456)
|
||||||
result2 := pointer.UnwarpOr(&b, "abc")
|
result2 := pointer.UnwrapOr(&b, "abc")
|
||||||
result3 := pointer.UnwarpOr(c, 456)
|
result3 := pointer.UnwrapOr(c, 456)
|
||||||
result4 := pointer.UnwarpOr(d, "def")
|
result4 := pointer.UnwrapOr(d, "def")
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
@@ -148,13 +148,13 @@ func main() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### <span id="UnwarpOrDefault">UnwarpOrDefault</span>
|
### <span id="UnwrapOrDefault">UnwrapOrDefault</span>
|
||||||
|
|
||||||
<p>Returns the value from the pointer or the default value if the pointer is nil.</p>
|
<p>Returns the value from the pointer or the default value if the pointer is nil.</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
```go
|
```go
|
||||||
UnwarpOrDefault[T any](p *T) T
|
UnwrapOrDefault[T any](p *T) T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
|
||||||
@@ -174,10 +174,10 @@ func main() {
|
|||||||
var c *int
|
var c *int
|
||||||
var d *string
|
var d *string
|
||||||
|
|
||||||
result1 := pointer.UnwarpOrDefault(&a)
|
result1 := pointer.UnwrapOrDefault(&a)
|
||||||
result2 := pointer.UnwarpOrDefault(&b)
|
result2 := pointer.UnwrapOrDefault(&b)
|
||||||
result3 := pointer.UnwarpOrDefault(c)
|
result3 := pointer.UnwrapOrDefault(c)
|
||||||
result4 := pointer.UnwarpOrDefault(d)
|
result4 := pointer.UnwrapOrDefault(d)
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ import (
|
|||||||
- [RetryFunc](#RetryFunc)
|
- [RetryFunc](#RetryFunc)
|
||||||
- [RetryDuration](#RetryDuration)
|
- [RetryDuration](#RetryDuration)
|
||||||
- [RetryTimes](#RetryTimes)
|
- [RetryTimes](#RetryTimes)
|
||||||
|
- [BackoffStrategy](#BackoffStrategy)
|
||||||
|
- [RetryWithCustomBackoff](#RetryWithCustomBackoff)
|
||||||
|
- [RetryWithLinearBackoff](#RetryWithLinearBackoff)
|
||||||
|
- [RetryWithExponentialWithJitterBackoff](#RetryWithExponentialWithJitterBackoff)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -259,3 +263,202 @@ func main() {
|
|||||||
// 3
|
// 3
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="BackoffStrategy">BackoffStrategy</span>
|
||||||
|
|
||||||
|
<p>An interface that defines a method for calculating backoff intervals.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
// BackoffStrategy is an interface that defines a method for calculating backoff intervals.
|
||||||
|
type BackoffStrategy interface {
|
||||||
|
// CalculateInterval returns the time.Duration after which the next retry attempt should be made.
|
||||||
|
CalculateInterval() time.Duration
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"github.com/duke-git/lancet/v2/retry"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExampleCustomBackoffStrategy struct {
|
||||||
|
interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
|
||||||
|
return c.interval + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
number := 0
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
if number == 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(number)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RetryWithCustomBackoff">RetryWithCustomBackoff</span>
|
||||||
|
|
||||||
|
<p>Set abitary custom backoff strategy.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"github.com/duke-git/lancet/v2/retry"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExampleCustomBackoffStrategy struct {
|
||||||
|
interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
|
||||||
|
return c.interval + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
number := 0
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
if number == 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(number)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="RetryWithLinearBackoff">RetryWithLinearBackoff</span>
|
||||||
|
|
||||||
|
<p>Set linear strategy backoff.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RetryWithLinearBackoff(interval time.Duration) Option
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</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")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := retry.Retry(increaseNumber, retry.RetryWithLinearBackoff(time.Microsecond*50))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(number)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="RetryWithExponentialWithJitterBackoff">RetryWithExponentialWithJitterBackoff</span>
|
||||||
|
|
||||||
|
<p>Set exponential strategy backoff.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</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")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := retry.Retry(increaseNumber, retry.RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(number)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import (
|
|||||||
- [DifferenceBy](#DifferenceBy)
|
- [DifferenceBy](#DifferenceBy)
|
||||||
- [DifferenceWith](#DifferenceWith)
|
- [DifferenceWith](#DifferenceWith)
|
||||||
- [DeleteAt](#DeleteAt)
|
- [DeleteAt](#DeleteAt)
|
||||||
|
- [DeleteRange](#DeleteRange)
|
||||||
- [Drop](#Drop)
|
- [Drop](#Drop)
|
||||||
- [DropRight](#DropRight)
|
- [DropRight](#DropRight)
|
||||||
- [DropWhile](#DropWhile)
|
- [DropWhile](#DropWhile)
|
||||||
@@ -85,6 +86,7 @@ import (
|
|||||||
- [ToSlicePointer](#ToSlicePointer)
|
- [ToSlicePointer](#ToSlicePointer)
|
||||||
- [Unique](#Unique)
|
- [Unique](#Unique)
|
||||||
- [UniqueBy](#UniqueBy)
|
- [UniqueBy](#UniqueBy)
|
||||||
|
- [UniqueByField](#UniqueByField)
|
||||||
- [Union](#Union)
|
- [Union](#Union)
|
||||||
- [UnionBy](#UnionBy)
|
- [UnionBy](#UnionBy)
|
||||||
- [UpdateAt](#UpdateAt)
|
- [UpdateAt](#UpdateAt)
|
||||||
@@ -92,6 +94,10 @@ import (
|
|||||||
- [KeyBy](#KeyBy)
|
- [KeyBy](#KeyBy)
|
||||||
- [Join](#Join)
|
- [Join](#Join)
|
||||||
- [Partition](#Partition)
|
- [Partition](#Partition)
|
||||||
|
- [SetToDefaultIf](#SetToDefaultIf)
|
||||||
|
- [Break](#Break)
|
||||||
|
- [RightPadding](#RightPadding)
|
||||||
|
- [LeftPadding](#LeftPadding)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -316,12 +322,12 @@ func main() {
|
|||||||
|
|
||||||
### <span id="Concat">Concat</span>
|
### <span id="Concat">Concat</span>
|
||||||
|
|
||||||
<p>Creates a new slice concatenating slice with any additional slices.</p>
|
<p>Concat creates a new slice concatenating slice with any additional slices.</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func Concat[T any](slice []T, slices ...[]T) []T
|
func Concat[T any](slices ...[]T) []T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
|
||||||
@@ -515,15 +521,15 @@ func main() {
|
|||||||
|
|
||||||
### <span id="DeleteAt">DeleteAt</span>
|
### <span id="DeleteAt">DeleteAt</span>
|
||||||
|
|
||||||
<p>Delete the element of slice from start index to end index - 1.</p>
|
<p>Delete delete the element of slice at index.</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func DeleteAt[T any](slice []T, start int, end ...int)
|
func DeleteAt[T any](slice []T, index int)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/pJ-d6MUWcvK)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/800B1dPBYyd)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
@@ -532,18 +538,66 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
result1 := slice.DeleteAt([]string{"a", "b", "c"}, -1)
|
chars := []string{"a", "b", "c", "d", "e"}
|
||||||
result2 := slice.DeleteAt([]string{"a", "b", "c"}, 0)
|
|
||||||
result3 := slice.DeleteAt([]string{"a", "b", "c"}, 0, 2)
|
result1 := slice.DeleteAt(chars, 0)
|
||||||
|
result2 := slice.DeleteAt(chars, 4)
|
||||||
|
result3 := slice.DeleteAt(chars, 5)
|
||||||
|
result4 := slice.DeleteAt(chars, 6)
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
fmt.Println(result3)
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [a b c]
|
// [b c d e]
|
||||||
// [b c]
|
// [a b c d]
|
||||||
// [c]
|
// [a b c d]
|
||||||
|
// [a b c d]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="DeleteRange">DeleteRange</span>
|
||||||
|
|
||||||
|
<p>Delete the element of slice from start index to end index(exclude)</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func DeleteRange[T any](slice []T, start, end int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/945HwiNrnle)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
chars := []string{"a", "b", "c", "d", "e"}
|
||||||
|
|
||||||
|
result1 := slice.DeleteRange(chars, 0, 0)
|
||||||
|
result2 := slice.DeleteRange(chars, 0, 1)
|
||||||
|
result3 := slice.DeleteRange(chars, 0, 3)
|
||||||
|
result4 := slice.DeleteRange(chars, 0, 4)
|
||||||
|
result5 := slice.DeleteRange(chars, 0, 5)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
fmt.Println(result5)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [a b c d e]
|
||||||
|
// [b c d e]
|
||||||
|
// [d e]
|
||||||
|
// [e]
|
||||||
|
// []
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1487,7 +1541,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="Merge">Merge</span>
|
### <span id="Merge">Merge(deprecated: use Concat)</span>
|
||||||
|
|
||||||
<p>Merge all given slices into one slice.</p>
|
<p>Merge all given slices into one slice.</p>
|
||||||
|
|
||||||
@@ -2257,6 +2311,47 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="UniqueByField">UniqueByField</span>
|
||||||
|
|
||||||
|
<p>Remove duplicate elements in struct slice by struct field.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func UniqueByField[T any](slice []T, field string) ([]T, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;"></span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []User{
|
||||||
|
{ID: 1, Name: "a"},
|
||||||
|
{ID: 2, Name: "b"},
|
||||||
|
{ID: 1, Name: "c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := slice.UniqueByField(users, "ID")
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [{1 a} {2 b}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="Union">Union</span>
|
### <span id="Union">Union</span>
|
||||||
|
|
||||||
<p>Creates a slice of unique values, in order, from all given slices. using == for equality comparisons.</p>
|
<p>Creates a slice of unique values, in order, from all given slices. using == for equality comparisons.</p>
|
||||||
@@ -2473,18 +2568,18 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
nums := []int{1, 2, 3, 4, 5}
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
result1 := slice.Partition(nums)
|
result1 := slice.Partition(nums)
|
||||||
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
|
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
|
||||||
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
|
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
fmt.Println(result3)
|
fmt.Println(result3)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [[1 2 3 4 5]]
|
// [[1 2 3 4 5]]
|
||||||
// [[2 4] [1 3 5]]
|
// [[2 4] [1 3 5]]
|
||||||
// [[1 2] [3 4] [5]]
|
// [[1 2] [3 4] [5]]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -2507,13 +2602,131 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
nums := []int{1, 2, 3, 4, 5}
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
val, idx := slice.Random(nums)
|
val, idx := slice.Random(nums)
|
||||||
if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) {
|
if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) {
|
||||||
fmt.Println("okk")
|
fmt.Println("okk")
|
||||||
}
|
}
|
||||||
|
// Output:
|
||||||
|
// okk
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="SetToDefaultIf">SetToDefaultIf</span>
|
||||||
|
|
||||||
|
<p>Sets elements to their default value if they match the given predicate. It retains the positions of the elements in the slice. It returns slice of T and the count of modified slice items</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/9AXGlPRC0-A)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||||
|
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||||
|
|
||||||
|
fmt.Println(modifiedStrs)
|
||||||
|
fmt.Println(count)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [ b c d ]
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Break">Break</span>
|
||||||
|
|
||||||
|
<p>Splits a slice into two based on a predicate function. It starts appending to the second slice after the first element that matches the predicate. All elements after the first match are included in the second slice, regardless of whether they match the predicate or not.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Break[T any](values []T, predicate func(T) bool) ([]T, []T)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/yLYcBTyeQIz)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
even := func(n int) bool { return n%2 == 0 }
|
||||||
|
|
||||||
|
resultEven, resultAfterFirstEven := slice.Break(nums, even)
|
||||||
|
|
||||||
|
fmt.Println(resultEven)
|
||||||
|
fmt.Println(resultAfterFirstEven)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1]
|
||||||
|
// [2 3 4 5]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<span id="c">RightPadding</span>
|
||||||
|
|
||||||
|
<p>RightPadding adds padding to the right end of a slice.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/0_2rlLEMBXL)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
padded := slice.RightPadding(nums, 0, 3)
|
||||||
|
fmt.Println(padded)
|
||||||
// Output:
|
// Output:
|
||||||
// okk
|
// [1 2 3 4 5 0 0 0]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<span id="LeftPadding">LeftPadding</span>
|
||||||
|
|
||||||
|
<p>LeftPadding adds padding to the left begin of a slice.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jlQVoelLl2k)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
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]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -60,6 +60,9 @@ import (
|
|||||||
- [ContainsAll](#ContainsAll)
|
- [ContainsAll](#ContainsAll)
|
||||||
- [ContainsAny](#ContainsAny)
|
- [ContainsAny](#ContainsAny)
|
||||||
- [RemoveWhiteSpace](#RemoveWhiteSpace)
|
- [RemoveWhiteSpace](#RemoveWhiteSpace)
|
||||||
|
- [SubInBetween](#SubInBetween)
|
||||||
|
- [HammingDistance](#HammingDistance)
|
||||||
|
- [Concat](#Concat)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -1463,3 +1466,104 @@ func main() {
|
|||||||
// hello world
|
// hello world
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="SubInBetween">SubInBetween</span>
|
||||||
|
|
||||||
|
<p>Return substring between the start and end position(excluded) of source string.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SubInBetween(str string, start string, end string) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/EDbaRvjeNsv)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/strutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
str := "abcde"
|
||||||
|
|
||||||
|
result1 := strutil.SubInBetween(str, "", "de")
|
||||||
|
result2 := strutil.SubInBetween(str, "a", "d")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// abc
|
||||||
|
// bc
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="HammingDistance">HammingDistance</span>
|
||||||
|
|
||||||
|
<p>HammingDistance calculates the Hamming distance between two strings. The Hamming distance is the number of positions at which the corresponding symbols are different.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func HammingDistance(a, b string) (int, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/glNdQEA9HUi)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/strutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
result1, _ := strutil.HammingDistance("de", "de")
|
||||||
|
result2, _ := strutil.HammingDistance("a", "d")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 0
|
||||||
|
// 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="Concat">Concat</span>
|
||||||
|
|
||||||
|
<p>Concatenates strings. <b>length</b> is the length of the concatenated string. If unsure, pass 0 or a negative number.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Concat(length int, str ...string) string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/strutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Hello World!
|
||||||
|
// Go Language
|
||||||
|
// An apple a day,keeps the doctor away
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -30,7 +30,7 @@ We are excited that you are interested in contributing to lancet. Before submitt
|
|||||||
|
|
||||||
- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass.
|
- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass.
|
||||||
|
|
||||||
- Make sure PRs are created to `v2` branch instead of `master` branch.
|
- Make sure PRs are created to `rc` branch instead of other branch.
|
||||||
|
|
||||||
- If your PR fixes a bug, please provide a description about the related bug.
|
- If your PR fixes a bug, please provide a description about the related bug.
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ features:
|
|||||||
details: Well structure, test for every exported function.
|
details: Well structure, test for every exported function.
|
||||||
---
|
---
|
||||||
|
|
||||||
<p style="position:relative; top: -316px;left: 560px;">
|
<p style="position:relative; inline-block;top: -330px;left: 30%">
|
||||||
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
|
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
|
||||||
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
|
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代
|
|||||||
|
|
||||||
- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。
|
- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。
|
||||||
|
|
||||||
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
|
- 确保 PR 是提交到 `rc` 分支,而不是其他分支。
|
||||||
|
|
||||||
- 如果是修复 bug,请在 PR 中给出描述信息。
|
- 如果是修复 bug,请在 PR 中给出描述信息。
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ features:
|
|||||||
details: 结构良好,测试每个导出的函数。
|
details: 结构良好,测试每个导出的函数。
|
||||||
---
|
---
|
||||||
|
|
||||||
<p style="position:relative;display: inline-block;top: -316px;left: 32%">
|
<p style="position:relative;display: inline-block;top: -330px;left: 30%">
|
||||||
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
|
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
|
||||||
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
|
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
1171
docs/package-lock.json
generated
1171
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,6 @@
|
|||||||
"docs:preview": "vitepress preview"
|
"docs:preview": "vitepress preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitepress": "^1.0.0-rc.4"
|
"vitepress": "^1.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
315
fileutil/file.go
315
fileutil/file.go
@@ -20,11 +20,68 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/validator"
|
"github.com/duke-git/lancet/v2/validator"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FileReader is a reader supporting offset seeking and reading one
|
||||||
|
// line at a time, this is especially useful for large files
|
||||||
|
type FileReader struct {
|
||||||
|
*bufio.Reader
|
||||||
|
file *os.File
|
||||||
|
offset int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileReader creates the FileReader struct for reading
|
||||||
|
func NewFileReader(path string) (*FileReader, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &FileReader{
|
||||||
|
file: f,
|
||||||
|
Reader: bufio.NewReader(f),
|
||||||
|
offset: 0,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadLine reads and returns one line at a time excluding the trailing '\r' and '\n'
|
||||||
|
func (f *FileReader) ReadLine() (string, error) {
|
||||||
|
data, err := f.Reader.ReadBytes('\n')
|
||||||
|
f.offset += int64(len(data))
|
||||||
|
if err == nil || err == io.EOF {
|
||||||
|
for len(data) > 0 && (data[len(data)-1] == '\r' || data[len(data)-1] == '\n') {
|
||||||
|
data = data[:len(data)-1]
|
||||||
|
}
|
||||||
|
return string(data), err
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset returns the current offset of the file
|
||||||
|
func (f *FileReader) Offset() int64 {
|
||||||
|
return f.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
// SeekOffset sets the current offset of the reading
|
||||||
|
func (f *FileReader) SeekOffset(offset int64) error {
|
||||||
|
_, err := f.file.Seek(offset, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Reader = bufio.NewReader(f.file)
|
||||||
|
f.offset = offset
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close takes care of the opened file
|
||||||
|
func (f *FileReader) Close() error {
|
||||||
|
return f.file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// IsExist checks if a file or directory exists.
|
// IsExist checks if a file or directory exists.
|
||||||
// Play: https://go.dev/play/p/nKKXt8ZQbmh
|
// Play: https://go.dev/play/p/nKKXt8ZQbmh
|
||||||
func IsExist(path string) bool {
|
func IsExist(path string) bool {
|
||||||
@@ -57,6 +114,50 @@ func CreateDir(absPath string) error {
|
|||||||
return os.MkdirAll(absPath, os.ModePerm)
|
return os.MkdirAll(absPath, os.ModePerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CopyDir copy src directory to dst directory, it will copy all files and directories recursively.
|
||||||
|
// the access permission will be the same as the source directory.
|
||||||
|
// if dstPath exists, it will return an error.
|
||||||
|
// Play: https://go.dev/play/p/YAyFTA_UuPb
|
||||||
|
func CopyDir(srcPath string, dstPath string) error {
|
||||||
|
srcInfo, err := os.Stat(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get source directory info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !srcInfo.IsDir() {
|
||||||
|
return fmt.Errorf("source path is not a directory: %s", srcPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.MkdirAll(dstPath, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create destination directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read source directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
srcDir := filepath.Join(srcPath, entry.Name())
|
||||||
|
dstDir := filepath.Join(dstPath, entry.Name())
|
||||||
|
|
||||||
|
if entry.IsDir() {
|
||||||
|
err := CopyDir(srcDir, dstDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := CopyFile(srcDir, dstDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsDir checks if the path is directory or not.
|
// IsDir checks if the path is directory or not.
|
||||||
// Play: https://go.dev/play/p/WkVwEKqtOWk
|
// Play: https://go.dev/play/p/WkVwEKqtOWk
|
||||||
func IsDir(path string) bool {
|
func IsDir(path string) bool {
|
||||||
@@ -321,7 +422,7 @@ func UnZip(zipFile string, destPath string) error {
|
|||||||
defer zipReader.Close()
|
defer zipReader.Close()
|
||||||
|
|
||||||
for _, f := range zipReader.File {
|
for _, f := range zipReader.File {
|
||||||
//issue#62: fix ZipSlip bug
|
// issue#62: fix ZipSlip bug
|
||||||
path, err := safeFilepathJoin(destPath, f.Name)
|
path, err := safeFilepathJoin(destPath, f.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -508,6 +609,25 @@ func FileSize(path string) (int64, error) {
|
|||||||
return f.Size(), nil
|
return f.Size(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DirSize walks the folder recursively and returns folder size in bytes.
|
||||||
|
func DirSize(path string) (int64, error) {
|
||||||
|
var size int64
|
||||||
|
err := filepath.WalkDir(path, func(_ string, d os.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !d.IsDir() {
|
||||||
|
info, err := d.Info()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
size += info.Size()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
|
|
||||||
// MTime returns file modified time.
|
// MTime returns file modified time.
|
||||||
// Play: https://go.dev/play/p/s_Tl7lZoAaY
|
// Play: https://go.dev/play/p/s_Tl7lZoAaY
|
||||||
func MTime(filepath string) (int64, error) {
|
func MTime(filepath string) (int64, error) {
|
||||||
@@ -536,7 +656,7 @@ func Sha(filepath string, shaType ...int) (string, error) {
|
|||||||
} else if shaType[0] == 512 {
|
} else if shaType[0] == 512 {
|
||||||
h = sha512.New()
|
h = sha512.New()
|
||||||
} else {
|
} else {
|
||||||
return "", errors.New("param `shaType` should be 1, 256 or 512.")
|
return "", errors.New("param `shaType` should be 1, 256 or 512")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,15 +674,19 @@ func Sha(filepath string, shaType ...int) (string, error) {
|
|||||||
|
|
||||||
// ReadCsvFile read file content into slice.
|
// ReadCsvFile read file content into slice.
|
||||||
// Play: https://go.dev/play/p/OExTkhGEd3_u
|
// Play: https://go.dev/play/p/OExTkhGEd3_u
|
||||||
func ReadCsvFile(filepath string) ([][]string, error) {
|
func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error) {
|
||||||
f, err := os.Open(filepath)
|
f, err := os.Open(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
csvReader := csv.NewReader(f)
|
reader := csv.NewReader(f)
|
||||||
records, err := csvReader.ReadAll()
|
if len(delimiter) > 0 {
|
||||||
|
reader.Comma = delimiter[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := reader.ReadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -571,8 +695,10 @@ func ReadCsvFile(filepath string) ([][]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteCsvFile write content to target csv file.
|
// WriteCsvFile write content to target csv file.
|
||||||
|
// append: append to existing csv file
|
||||||
|
// delimiter: specifies csv delimiter
|
||||||
// Play: https://go.dev/play/p/dAXm58Q5U1o
|
// Play: https://go.dev/play/p/dAXm58Q5U1o
|
||||||
func WriteCsvFile(filepath string, records [][]string, append bool) error {
|
func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error {
|
||||||
flag := os.O_RDWR | os.O_CREATE
|
flag := os.O_RDWR | os.O_CREATE
|
||||||
|
|
||||||
if append {
|
if append {
|
||||||
@@ -587,7 +713,19 @@ func WriteCsvFile(filepath string, records [][]string, append bool) error {
|
|||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
writer := csv.NewWriter(f)
|
writer := csv.NewWriter(f)
|
||||||
writer.Comma = ','
|
// 设置默认分隔符为逗号,除非另外指定
|
||||||
|
if len(delimiter) > 0 {
|
||||||
|
writer.Comma = delimiter[0]
|
||||||
|
} else {
|
||||||
|
writer.Comma = ','
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历所有记录并处理包含分隔符或双引号的单元格
|
||||||
|
for i := range records {
|
||||||
|
for j := range records[i] {
|
||||||
|
records[i][j] = escapeCSVField(records[i][j], writer.Comma)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return writer.WriteAll(records)
|
return writer.WriteAll(records)
|
||||||
}
|
}
|
||||||
@@ -646,3 +784,166 @@ func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error) {
|
|||||||
return nil, func() {}, errors.New("unknown file type")
|
return nil, func() {}, errors.New("unknown file type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// escapeCSVField 处理单元格内容,如果包含分隔符,则用双引号包裹
|
||||||
|
func escapeCSVField(field string, delimiter rune) string {
|
||||||
|
// 替换所有的双引号为两个双引号
|
||||||
|
escapedField := strings.ReplaceAll(field, "\"", "\"\"")
|
||||||
|
|
||||||
|
// 如果字段包含分隔符、双引号或换行符,用双引号包裹整个字段
|
||||||
|
if strings.ContainsAny(escapedField, string(delimiter)+"\"\n") {
|
||||||
|
escapedField = fmt.Sprintf("\"%s\"", escapedField)
|
||||||
|
}
|
||||||
|
|
||||||
|
return escapedField
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteMapsToCsv write slice of map to csv file.
|
||||||
|
// Play: https://go.dev/play/p/umAIomZFV1c
|
||||||
|
// filepath: Path to the CSV file.
|
||||||
|
// records: Slice of maps to be written. the value of map should be basic type.
|
||||||
|
// the maps will be sorted by key in alphabeta order, then be written into csv file.
|
||||||
|
// appendToExistingFile: If true, data will be appended to the file if it exists.
|
||||||
|
// delimiter: Delimiter to use in the CSV file.
|
||||||
|
// headers: order of the csv column headers, needs to be consistent with the key of the map.
|
||||||
|
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune,
|
||||||
|
headers ...[]string) error {
|
||||||
|
for _, record := range records {
|
||||||
|
for _, value := range record {
|
||||||
|
if !isCsvSupportedType(value) {
|
||||||
|
return errors.New("unsupported value type detected; only basic types are supported: \nbool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var columnHeaders []string
|
||||||
|
if len(headers) > 0 {
|
||||||
|
columnHeaders = headers[0]
|
||||||
|
} else {
|
||||||
|
for key := range records[0] {
|
||||||
|
columnHeaders = append(columnHeaders, key)
|
||||||
|
}
|
||||||
|
// sort keys in alphabeta order
|
||||||
|
sort.Strings(columnHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
var datasToWrite [][]string
|
||||||
|
if !appendToExistingFile {
|
||||||
|
datasToWrite = append(datasToWrite, columnHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range records {
|
||||||
|
var row []string
|
||||||
|
for _, h := range columnHeaders {
|
||||||
|
row = append(row, fmt.Sprintf("%v", record[h]))
|
||||||
|
}
|
||||||
|
datasToWrite = append(datasToWrite, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
return WriteCsvFile(filepath, datasToWrite, appendToExistingFile, delimiter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the value of map which to be written into csv is basic type.
|
||||||
|
func isCsvSupportedType(v interface{}) bool {
|
||||||
|
switch v.(type) {
|
||||||
|
case bool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChunkRead reads a block from the file at the specified offset and returns all lines within the block
|
||||||
|
// Play: https://go.dev/play/p/r0hPmKWhsgf
|
||||||
|
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error) {
|
||||||
|
buf := bufPool.Get().([]byte)[:size] // 从Pool获取缓冲区并调整大小
|
||||||
|
n, err := file.ReadAt(buf, offset) // 从指定偏移读取数据到缓冲区
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf = buf[:n] // 调整切片以匹配实际读取的字节数
|
||||||
|
|
||||||
|
var lines []string
|
||||||
|
var lineStart int
|
||||||
|
for i, b := range buf {
|
||||||
|
if b == '\n' {
|
||||||
|
line := string(buf[lineStart:i]) // 不包括换行符
|
||||||
|
lines = append(lines, line)
|
||||||
|
lineStart = i + 1 // 设置下一行的开始
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lineStart < len(buf) { // 处理块末尾的行
|
||||||
|
line := string(buf[lineStart:])
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
bufPool.Put(buf) // 读取完成后,将缓冲区放回Pool
|
||||||
|
return lines, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParallelChunkRead reads the file in parallel and send each chunk of lines to the specified channel.
|
||||||
|
// filePath 文件路径
|
||||||
|
// chunkSizeMB 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整
|
||||||
|
// maxGoroutine 并发读取分块的数量,设置为0时使用CPU核心数
|
||||||
|
// linesCh用于接收返回结果的通道。
|
||||||
|
// Play: https://go.dev/play/p/teMXnCsdSEw
|
||||||
|
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error {
|
||||||
|
if chunkSizeMB == 0 {
|
||||||
|
chunkSizeMB = 100
|
||||||
|
}
|
||||||
|
chunkSize := chunkSizeMB * 1024 * 1024
|
||||||
|
// 内存复用
|
||||||
|
bufPool := sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, 0, chunkSize)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxGoroutine == 0 {
|
||||||
|
maxGoroutine = runtime.NumCPU() // 设置为0时使用CPU核心数
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
info, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
chunkOffsetCh := make(chan int64, maxGoroutine)
|
||||||
|
|
||||||
|
// 分配工作
|
||||||
|
go func() {
|
||||||
|
for i := int64(0); i < info.Size(); i += int64(chunkSize) {
|
||||||
|
chunkOffsetCh <- i
|
||||||
|
}
|
||||||
|
close(chunkOffsetCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 启动工作协程
|
||||||
|
for i := 0; i < maxGoroutine; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
for chunkOffset := range chunkOffsetCh {
|
||||||
|
chunk, err := ChunkRead(f, chunkOffset, chunkSize, &bufPool)
|
||||||
|
if err == nil {
|
||||||
|
linesCh <- chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待所有解析完成后关闭行通道
|
||||||
|
wg.Wait()
|
||||||
|
close(linesCh)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ package fileutil
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleIsExist() {
|
func ExampleIsExist() {
|
||||||
@@ -331,6 +334,28 @@ func ExampleWriteCsvFile() {
|
|||||||
// [[Lili 22 female] [Jim 21 male]]
|
// [[Lili 22 female] [Jim 21 male]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleWriteMapsToCsv() {
|
||||||
|
csvFilePath := "./testdata/test3.csv"
|
||||||
|
records := []map[string]any{
|
||||||
|
{"Name": "Lili", "Age": "22", "Gender": "female"},
|
||||||
|
{"Name": "Jim", "Age": "21", "Gender": "male"},
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := []string{"Name", "Age", "Gender"}
|
||||||
|
err := WriteMapsToCsv(csvFilePath, records, false, ';', headers)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := ReadCsvFile(csvFilePath, ';')
|
||||||
|
|
||||||
|
fmt.Println(content)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleWriteStringToFile() {
|
func ExampleWriteStringToFile() {
|
||||||
filepath := "./test.txt"
|
filepath := "./test.txt"
|
||||||
|
|
||||||
@@ -398,8 +423,69 @@ func ExampleReadFile() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(string(dat))
|
fmt.Println(string(dat))
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// User-agent: *
|
// User-agent: *
|
||||||
// Disallow: /deny
|
// Disallow: /deny
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleChunkRead() {
|
||||||
|
const mb = 1024 * 1024
|
||||||
|
const defaultChunkSizeMB = 100
|
||||||
|
|
||||||
|
filePath := "./testdata/test1.csv"
|
||||||
|
f, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var bufPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, 0, defaultChunkSizeMB*mb)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
lines, err := ChunkRead(f, 0, 100, &bufPool)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(lines[0])
|
||||||
|
fmt.Println(lines[1])
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Lili,22,female
|
||||||
|
// Jim,21,male
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleParallelChunkRead() {
|
||||||
|
const mb = 1024 * 1024
|
||||||
|
const defaultChunkSizeMB = 100 // 默认值
|
||||||
|
|
||||||
|
numParsers := runtime.NumCPU()
|
||||||
|
|
||||||
|
linesCh := make(chan []string, numParsers)
|
||||||
|
filePath := "./testdata/test1.csv"
|
||||||
|
|
||||||
|
go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
|
||||||
|
|
||||||
|
var totalLines int
|
||||||
|
for lines := range linesCh {
|
||||||
|
totalLines += len(lines)
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
fmt.Println(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(totalLines)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Lili,22,female
|
||||||
|
// Jim,21,male
|
||||||
|
// 2
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ package fileutil
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
@@ -340,10 +344,14 @@ func TestSha(t *testing.T) {
|
|||||||
assert := internal.NewAssert(t, "TestSha")
|
assert := internal.NewAssert(t, "TestSha")
|
||||||
|
|
||||||
sha1, err := Sha("./testdata/test.txt", 1)
|
sha1, err := Sha("./testdata/test.txt", 1)
|
||||||
sha256, err := Sha("./testdata/test.txt", 256)
|
|
||||||
sha512, err := Sha("./testdata/test.txt", 512)
|
|
||||||
|
|
||||||
assert.IsNil(err)
|
assert.IsNil(err)
|
||||||
|
|
||||||
|
sha256, err := Sha("./testdata/test.txt", 256)
|
||||||
|
assert.IsNil(err)
|
||||||
|
|
||||||
|
sha512, err := Sha("./testdata/test.txt", 512)
|
||||||
|
assert.IsNil(err)
|
||||||
|
|
||||||
assert.Equal("dda3cf10c5a6ff6c6659a497bf7261b287af2bc7", sha1)
|
assert.Equal("dda3cf10c5a6ff6c6659a497bf7261b287af2bc7", sha1)
|
||||||
assert.Equal("aa6d0a3fbc3442c228d606da09e0c1dc98c69a1cac3da1909199e0266171df35", sha256)
|
assert.Equal("aa6d0a3fbc3442c228d606da09e0c1dc98c69a1cac3da1909199e0266171df35", sha256)
|
||||||
assert.Equal("d22aba2a1b7a2e2f512756255cc1c3708905646920cb1eb95e45b531ba74774dbbb89baebf1f716220eb9cf4908f1cfc5b2a01267704d9a59f59d77cab609870", sha512)
|
assert.Equal("d22aba2a1b7a2e2f512756255cc1c3708905646920cb1eb95e45b531ba74774dbbb89baebf1f716220eb9cf4908f1cfc5b2a01267704d9a59f59d77cab609870", sha512)
|
||||||
@@ -379,11 +387,36 @@ func TestWriteCsvFile(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(2, len(readContent))
|
assert.Equal(2, len(readContent))
|
||||||
assert.Equal(3, len(readContent[0]))
|
assert.Equal(3, len(readContent[0]))
|
||||||
assert.Equal("Lili", content[0][0])
|
assert.Equal("Lili", readContent[0][0])
|
||||||
|
|
||||||
// RemoveFile(csvFilePath)
|
// RemoveFile(csvFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWriteMapsToCsv(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestWriteMapsToCSV")
|
||||||
|
|
||||||
|
csvFilePath := "./testdata/test4.csv"
|
||||||
|
records := []map[string]any{
|
||||||
|
{"Name": "Lili", "Age": "22", "Gender": "female"},
|
||||||
|
{"Name": "Jim", "Age": "21", "Gender": "male"},
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := []string{"Name", "Age", "Gender"}
|
||||||
|
err := WriteMapsToCsv(csvFilePath, records, false, ';', headers)
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
|
||||||
|
content, err := ReadCsvFile(csvFilePath, ';')
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
|
||||||
|
assert.Equal(3, len(content))
|
||||||
|
assert.Equal(3, len(content[0]))
|
||||||
|
assert.Equal("Lili", content[1][0])
|
||||||
|
assert.Equal("22", content[1][1])
|
||||||
|
assert.Equal("female", content[1][2])
|
||||||
|
}
|
||||||
|
|
||||||
func TestWriteStringToFile(t *testing.T) {
|
func TestWriteStringToFile(t *testing.T) {
|
||||||
assert := internal.NewAssert(t, "TestWriteStringToFile")
|
assert := internal.NewAssert(t, "TestWriteStringToFile")
|
||||||
|
|
||||||
@@ -476,3 +509,113 @@ Disallow: /deny
|
|||||||
`
|
`
|
||||||
internal.NewAssert(t, "TestReadFile").Equal(want, string(dat))
|
internal.NewAssert(t, "TestReadFile").Equal(want, string(dat))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReadlineFile(t *testing.T) {
|
||||||
|
path := "./testdata/demo.csv"
|
||||||
|
reader, err := NewFileReader(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
indexMap := make(map[string]int64)
|
||||||
|
defer reader.Close()
|
||||||
|
for {
|
||||||
|
offset := reader.Offset()
|
||||||
|
line, err := reader.ReadLine()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
indexMap[line] = offset
|
||||||
|
}
|
||||||
|
|
||||||
|
lines, err := ReadFileByLine(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
for _, line := range lines {
|
||||||
|
offset, ok := indexMap[line]
|
||||||
|
if !ok {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if err = reader.SeekOffset(offset); err != nil {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
lineRead, err := reader.ReadLine()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
internal.NewAssert(t, "TestReadlineFile").Equal(line, lineRead)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyDir(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestCopyDir")
|
||||||
|
|
||||||
|
src := "./testdata"
|
||||||
|
dest := "./testdata_copy"
|
||||||
|
|
||||||
|
err := CopyDir(src, dest)
|
||||||
|
assert.IsNil(err)
|
||||||
|
|
||||||
|
assert.Equal(true, IsExist(dest))
|
||||||
|
|
||||||
|
filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
|
||||||
|
destPath := strings.Replace(path, src, dest, 1)
|
||||||
|
assert.Equal(true, IsExist(destPath))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
os.RemoveAll(dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParallelChunkRead(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestParallelChunkRead")
|
||||||
|
|
||||||
|
const mb = 1024 * 1024
|
||||||
|
const defaultChunkSizeMB = 100 // 默认值
|
||||||
|
|
||||||
|
numParsers := runtime.NumCPU()
|
||||||
|
|
||||||
|
linesCh := make(chan []string, numParsers)
|
||||||
|
filePath := "./testdata/test1.csv" // 替换为你的文件路径
|
||||||
|
|
||||||
|
go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
|
||||||
|
|
||||||
|
var totalLines int
|
||||||
|
for lines := range linesCh {
|
||||||
|
totalLines += len(lines)
|
||||||
|
|
||||||
|
assert.Equal("Lili,22,female", lines[0])
|
||||||
|
assert.Equal("Jim,21,male", lines[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(2, totalLines)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChunkRead(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestChunkRead")
|
||||||
|
|
||||||
|
const mb = 1024 * 1024
|
||||||
|
const defaultChunkSizeMB = 100 // 默认值
|
||||||
|
|
||||||
|
filePath := "./testdata/test1.csv" // 替换为你的文件路径
|
||||||
|
f, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var bufPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, 0, defaultChunkSizeMB*mb)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
lines, err := ChunkRead(f, 0, 100, &bufPool)
|
||||||
|
|
||||||
|
assert.Equal("Lili,22,female", lines[0])
|
||||||
|
assert.Equal("Jim,21,male", lines[1])
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
1
fileutil/testdata/test01/demo2.csv
vendored
Normal file
1
fileutil/testdata/test01/demo2.csv
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
makj1
|
||||||
|
1
fileutil/testdata/test1.csv
vendored
1
fileutil/testdata/test1.csv
vendored
@@ -1,3 +1,2 @@
|
|||||||
Lili,22,female
|
Lili,22,female
|
||||||
Jim,21,male
|
Jim,21,male
|
||||||
|
|
||||||
|
|||||||
|
2
fileutil/testdata/test2.csv
vendored
2
fileutil/testdata/test2.csv
vendored
@@ -1,3 +1,5 @@
|
|||||||
Lili,22,female
|
Lili,22,female
|
||||||
Jim,21,male
|
Jim,21,male
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
3
fileutil/testdata/test3.csv
vendored
Normal file
3
fileutil/testdata/test3.csv
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Name;Age;Gender
|
||||||
|
Lili;22;female
|
||||||
|
Jim;21;male
|
||||||
|
3
fileutil/testdata/test4.csv
vendored
Normal file
3
fileutil/testdata/test4.csv
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Name;Age;Gender
|
||||||
|
Lili;22;female
|
||||||
|
Jim;21;male
|
||||||
|
@@ -6,12 +6,11 @@ package formatter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/convertor"
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
"github.com/duke-git/lancet/v2/strutil"
|
|
||||||
"github.com/duke-git/lancet/v2/validator"
|
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,31 +19,24 @@ import (
|
|||||||
// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345"
|
// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345"
|
||||||
// Play: https://go.dev/play/p/eRD5k2vzUVX
|
// 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, symbol string) string {
|
||||||
if validator.IsInt(value) {
|
numString := convertor.ToString(value)
|
||||||
v, err := convertor.ToInt(value)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return symbol + commaInt(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if validator.IsFloat(value) {
|
_, err := strconv.ParseFloat(numString, 64)
|
||||||
v, err := convertor.ToFloat(value)
|
if err != nil {
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return symbol + commaFloat(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strutil.IsString(value) {
|
|
||||||
v := fmt.Sprintf("%v", value)
|
|
||||||
if validator.IsNumberStr(v) {
|
|
||||||
return symbol + commaStr(v)
|
|
||||||
}
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
index := strings.Index(numString, ".")
|
||||||
|
if index == -1 {
|
||||||
|
index = len(numString)
|
||||||
|
}
|
||||||
|
|
||||||
|
for index > 3 {
|
||||||
|
index = index - 3
|
||||||
|
numString = numString[:index] + "," + numString[index:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbol + numString
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pretty data to JSON string.
|
// Pretty data to JSON string.
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
package formatter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// see https://github.com/dustin/go-humanize/blob/master/comma.go
|
|
||||||
func commaInt(v int64) string {
|
|
||||||
sign := ""
|
|
||||||
|
|
||||||
// Min int64 can't be negated to a usable value, so it has to be special cased.
|
|
||||||
if v == math.MinInt64 {
|
|
||||||
return "-9,223,372,036,854,775,808"
|
|
||||||
}
|
|
||||||
|
|
||||||
if v < 0 {
|
|
||||||
sign = "-"
|
|
||||||
v = 0 - v
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := []string{"", "", "", "", "", "", ""}
|
|
||||||
j := len(parts) - 1
|
|
||||||
|
|
||||||
for v > 999 {
|
|
||||||
parts[j] = strconv.FormatInt(v%1000, 10)
|
|
||||||
switch len(parts[j]) {
|
|
||||||
case 2:
|
|
||||||
parts[j] = "0" + parts[j]
|
|
||||||
case 1:
|
|
||||||
parts[j] = "00" + parts[j]
|
|
||||||
}
|
|
||||||
v = v / 1000
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
parts[j] = strconv.Itoa(int(v))
|
|
||||||
return sign + strings.Join(parts[j:], ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
func commaFloat(v float64) string {
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
if v < 0 {
|
|
||||||
buf.Write([]byte{'-'})
|
|
||||||
v = 0 - v
|
|
||||||
}
|
|
||||||
|
|
||||||
comma := []byte{','}
|
|
||||||
|
|
||||||
parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".")
|
|
||||||
pos := 0
|
|
||||||
if len(parts[0])%3 != 0 {
|
|
||||||
pos += len(parts[0]) % 3
|
|
||||||
buf.WriteString(parts[0][:pos])
|
|
||||||
buf.Write(comma)
|
|
||||||
}
|
|
||||||
for ; pos < len(parts[0]); pos += 3 {
|
|
||||||
buf.WriteString(parts[0][pos : pos+3])
|
|
||||||
buf.Write(comma)
|
|
||||||
}
|
|
||||||
buf.Truncate(buf.Len() - 1)
|
|
||||||
|
|
||||||
if len(parts) > 1 {
|
|
||||||
buf.Write([]byte{'.'})
|
|
||||||
buf.WriteString(parts[1])
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func commaStr(s string) string {
|
|
||||||
dotIndex := strings.Index(s, ".")
|
|
||||||
if dotIndex != -1 {
|
|
||||||
return commaStrRecursive(s[:dotIndex]) + s[dotIndex:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return commaStrRecursive(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func commaStrRecursive(s string) string {
|
|
||||||
if len(s) <= 3 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return commaStrRecursive(s[:len(s)-3]) + "," + commaStrRecursive(s[len(s)-3:])
|
|
||||||
}
|
|
||||||
@@ -136,6 +136,28 @@ func Pipeline[T any](funcs ...func(T) T) func(T) T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
|
||||||
|
// A predicate function that takes an argument of type T and returns a bool.
|
||||||
|
// An apply function that also takes an argument of type T and returns a modified value of the same type.
|
||||||
|
// Play: https://go.dev/play/p/XlXHHtzCf7d
|
||||||
|
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool) {
|
||||||
|
if predicate == nil {
|
||||||
|
panic("programming error: predicate must be not nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if apply == nil {
|
||||||
|
panic("programming error: apply must be not nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(t T) (T, bool) {
|
||||||
|
if !predicate(t) {
|
||||||
|
var defaultValue T
|
||||||
|
return defaultValue, false
|
||||||
|
}
|
||||||
|
return apply(t), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func unsafeInvokeFunc(fn any, args ...any) []reflect.Value {
|
func unsafeInvokeFunc(fn any, args ...any) []reflect.Value {
|
||||||
fv := reflect.ValueOf(fn)
|
fv := reflect.ValueOf(fn)
|
||||||
params := make([]reflect.Value, len(args))
|
params := make([]reflect.Value, len(args))
|
||||||
|
|||||||
@@ -145,3 +145,32 @@ func ExamplePipeline() {
|
|||||||
// Output:
|
// Output:
|
||||||
// 36
|
// 36
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleAcceptIf() {
|
||||||
|
|
||||||
|
adder := AcceptIf(
|
||||||
|
And(
|
||||||
|
func(x int) bool {
|
||||||
|
return x > 10
|
||||||
|
}, func(x int) bool {
|
||||||
|
return x%2 == 0
|
||||||
|
}),
|
||||||
|
func(x int) int {
|
||||||
|
return x + 1
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
result, ok := adder(20)
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(ok)
|
||||||
|
|
||||||
|
result, ok = adder(21)
|
||||||
|
fmt.Println(result)
|
||||||
|
fmt.Println(ok)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 21
|
||||||
|
// true
|
||||||
|
// 0
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func TestBefore(t *testing.T) {
|
|||||||
|
|
||||||
var res []int64
|
var res []int64
|
||||||
type cb func(args ...any) []reflect.Value
|
type cb func(args ...any) []reflect.Value
|
||||||
appendStr := func(i int, s string, fn cb) {
|
appendStr := func(i int, _ string, fn cb) {
|
||||||
v := fn(i)
|
v := fn(i)
|
||||||
res = append(res, v[0].Int())
|
res = append(res, v[0].Int())
|
||||||
}
|
}
|
||||||
@@ -162,3 +162,63 @@ func TestPipeline(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(36, f(2))
|
assert.Equal(36, f(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAcceptIf(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "AcceptIf")
|
||||||
|
|
||||||
|
adder := AcceptIf(
|
||||||
|
And(
|
||||||
|
func(x int) bool {
|
||||||
|
return x > 10
|
||||||
|
}, func(x int) bool {
|
||||||
|
return x%2 == 0
|
||||||
|
}),
|
||||||
|
func(x int) int {
|
||||||
|
return x + 1
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
result, ok := adder(20)
|
||||||
|
assert.Equal(21, result)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
result, ok = adder(21)
|
||||||
|
assert.Equal(0, result)
|
||||||
|
assert.Equal(false, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAcceptIfPanicMissingPredicate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestAcceptIfPanicMissingPredicate")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
v := recover()
|
||||||
|
assert.Equal("programming error: predicate must be not nil", v)
|
||||||
|
}()
|
||||||
|
|
||||||
|
AcceptIf(
|
||||||
|
nil,
|
||||||
|
func(x int) int {
|
||||||
|
return x
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAcceptIfPanicMissingApply(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestAcceptIfPanicMissingApply")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
v := recover()
|
||||||
|
assert.Equal("programming error: apply must be not nil", v)
|
||||||
|
}()
|
||||||
|
|
||||||
|
AcceptIf(
|
||||||
|
func(i int) bool {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
97
function/predicate.go
Normal file
97
function/predicate.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package function
|
||||||
|
|
||||||
|
// And returns a composed predicate that represents the logical AND of a list of predicates.
|
||||||
|
// It evaluates to true only if all predicates evaluate to true for the given value.
|
||||||
|
// Play: https://go.dev/play/p/dTBHJMQ0zD2
|
||||||
|
func And[T any](predicates ...func(T) bool) func(T) bool {
|
||||||
|
if len(predicates) < 2 {
|
||||||
|
panic("programming error: predicates count must be at least 2")
|
||||||
|
}
|
||||||
|
return func(value T) bool {
|
||||||
|
for _, predicate := range predicates {
|
||||||
|
if !predicate(value) {
|
||||||
|
return false // Short-circuit on the first false predicate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true // True if all predicates are true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nand returns a composed predicate that represents the logical NAND of a list of predicates.
|
||||||
|
// It evaluates to true only if all predicates evaluate to false for the given value.
|
||||||
|
// Play: https://go.dev/play/p/Rb-FdNGpgSO
|
||||||
|
func Nand[T any](predicates ...func(T) bool) func(T) bool {
|
||||||
|
if len(predicates) < 2 {
|
||||||
|
panic("programming error: predicates count must be at least 2")
|
||||||
|
}
|
||||||
|
return func(value T) bool {
|
||||||
|
for _, predicate := range predicates {
|
||||||
|
if predicate(value) {
|
||||||
|
return false // Short-circuit on the first true predicate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true // True if all predicates are false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negate returns a predicate that represents the logical negation of this predicate.
|
||||||
|
// Play: https://go.dev/play/p/jbI8BtgFnVE
|
||||||
|
func Negate[T any](predicate func(T) bool) func(T) bool {
|
||||||
|
return func(value T) bool {
|
||||||
|
return !predicate(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or returns a composed predicate that represents the logical OR of a list of predicates.
|
||||||
|
// It evaluates to true if at least one of the predicates evaluates to true for the given value.
|
||||||
|
// Play: https://go.dev/play/p/LitCIsDFNDA
|
||||||
|
func Or[T any](predicates ...func(T) bool) func(T) bool {
|
||||||
|
if len(predicates) < 2 {
|
||||||
|
panic("programming error: predicates count must be at least 2")
|
||||||
|
}
|
||||||
|
return func(value T) bool {
|
||||||
|
for _, predicate := range predicates {
|
||||||
|
if predicate(value) {
|
||||||
|
return true // Short-circuit on the first true predicate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false // False if all predicates are false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nor returns a composed predicate that represents the logical NOR of a list of predicates.
|
||||||
|
// It evaluates to true only if all predicates evaluate to false for the given value.
|
||||||
|
// Play: https://go.dev/play/p/2KdCoBEOq84
|
||||||
|
func Nor[T any](predicates ...func(T) bool) func(T) bool {
|
||||||
|
if len(predicates) < 2 {
|
||||||
|
panic("programming error: predicates count must be at least 2")
|
||||||
|
}
|
||||||
|
return func(value T) bool {
|
||||||
|
for _, predicate := range predicates {
|
||||||
|
if predicate(value) {
|
||||||
|
return false // If any predicate evaluates to true, the NOR result is false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true // Only returns true if all predicates evaluate to false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Xnor returns a composed predicate that represents the logical XNOR of a list of predicates.
|
||||||
|
// It evaluates to true only if all predicates evaluate to true or false for the given value.
|
||||||
|
// Play: https://go.dev/play/p/FJxko8SFbqc
|
||||||
|
func Xnor[T any](predicates ...func(T) bool) func(T) bool {
|
||||||
|
if len(predicates) < 2 {
|
||||||
|
panic("programming error: predicates count must be at least 2")
|
||||||
|
}
|
||||||
|
return func(value T) bool {
|
||||||
|
trueCount := 0
|
||||||
|
for _, predicate := range predicates {
|
||||||
|
if predicate(value) {
|
||||||
|
trueCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// XNOR is true if either all predicates are true or all are false
|
||||||
|
// This is the same as saying trueCount is 0 (all false) or trueCount is len(predicates) (all true)
|
||||||
|
return trueCount == 0 || trueCount == len(predicates)
|
||||||
|
}
|
||||||
|
}
|
||||||
128
function/predicate_example_test.go
Normal file
128
function/predicate_example_test.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package function
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleNegate() {
|
||||||
|
// Define some simple predicates for demonstration
|
||||||
|
isUpperCase := func(s string) bool {
|
||||||
|
return strings.ToUpper(s) == s
|
||||||
|
}
|
||||||
|
isLowerCase := func(s string) bool {
|
||||||
|
return strings.ToLower(s) == s
|
||||||
|
}
|
||||||
|
isMixedCase := Negate(Or(isUpperCase, isLowerCase))
|
||||||
|
|
||||||
|
fmt.Println(isMixedCase("ABC"))
|
||||||
|
fmt.Println(isMixedCase("AbC"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleOr() {
|
||||||
|
containsDigitOrSpecialChar := Or(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(containsDigitOrSpecialChar("hello!"))
|
||||||
|
fmt.Println(containsDigitOrSpecialChar("hello"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleAnd() {
|
||||||
|
isNumericAndLength5 := And(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(isNumericAndLength5("12345"))
|
||||||
|
fmt.Println(isNumericAndLength5("1234"))
|
||||||
|
fmt.Println(isNumericAndLength5("abcde"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleNand() {
|
||||||
|
isNumericAndLength5 := Nand(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(isNumericAndLength5("12345"))
|
||||||
|
fmt.Println(isNumericAndLength5("1234"))
|
||||||
|
fmt.Println(isNumericAndLength5("abcdef"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleNor() {
|
||||||
|
match := Nor(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(match("dbcdckkeee"))
|
||||||
|
|
||||||
|
match = Nor(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(match("0123456789"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleXnor() {
|
||||||
|
isEven := func(i int) bool { return i%2 == 0 }
|
||||||
|
isPositive := func(i int) bool { return i > 0 }
|
||||||
|
|
||||||
|
match := Xnor(isEven, isPositive)
|
||||||
|
|
||||||
|
fmt.Println(match(2))
|
||||||
|
fmt.Println(match(-3))
|
||||||
|
fmt.Println(match(3))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
// false
|
||||||
|
}
|
||||||
|
|
||||||
|
// func ExamplePredicatesMix() {
|
||||||
|
// a := Or(
|
||||||
|
// func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
// func(s string) bool { return strings.ContainsAny(s, "!") },
|
||||||
|
// )
|
||||||
|
|
||||||
|
// b := And(
|
||||||
|
// func(s string) bool { return strings.ContainsAny(s, "hello") },
|
||||||
|
// func(s string) bool { return strings.ContainsAny(s, "!") },
|
||||||
|
// )
|
||||||
|
|
||||||
|
// c := Negate(And(a, b))
|
||||||
|
// fmt.Println(c("hello!"))
|
||||||
|
|
||||||
|
// c = Nor(a, b)
|
||||||
|
// fmt.Println(c("hello!"))
|
||||||
|
|
||||||
|
// // Output:
|
||||||
|
// // false
|
||||||
|
// // false
|
||||||
|
// }
|
||||||
134
function/predicate_test.go
Normal file
134
function/predicate_test.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package function
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPredicatesNegatePure(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestPredicatesNegatePure")
|
||||||
|
|
||||||
|
// Define some simple predicates for demonstration
|
||||||
|
isUpperCase := func(s string) bool {
|
||||||
|
return strings.ToUpper(s) == s
|
||||||
|
}
|
||||||
|
isLowerCase := func(s string) bool {
|
||||||
|
return strings.ToLower(s) == s
|
||||||
|
}
|
||||||
|
isMixedCase := Negate(Or(isUpperCase, isLowerCase))
|
||||||
|
|
||||||
|
assert.ShouldBeFalse(isMixedCase("ABC"))
|
||||||
|
assert.ShouldBeTrue(isMixedCase("AbC"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPredicatesOrPure(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestPredicatesOrPure")
|
||||||
|
|
||||||
|
containsDigitOrSpecialChar := Or(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ShouldBeTrue(containsDigitOrSpecialChar("hello!"))
|
||||||
|
assert.ShouldBeFalse(containsDigitOrSpecialChar("hello"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPredicatesAndPure(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestPredicatesAndPure")
|
||||||
|
|
||||||
|
isNumericAndLength5 := And(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ShouldBeTrue(isNumericAndLength5("12345"))
|
||||||
|
assert.ShouldBeFalse(isNumericAndLength5("1234"))
|
||||||
|
assert.ShouldBeFalse(isNumericAndLength5("abcde"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPredicatesNandPure(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestPredicatesNandPure")
|
||||||
|
|
||||||
|
isNumericAndLength5 := Nand(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ShouldBeFalse(isNumericAndLength5("12345"))
|
||||||
|
assert.ShouldBeFalse(isNumericAndLength5("1234"))
|
||||||
|
assert.ShouldBeTrue(isNumericAndLength5("abcdef"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPredicatesNorPure(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestPredicatesNorPure")
|
||||||
|
|
||||||
|
match := Nor(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ShouldBeTrue(match("dbcdckkeee"))
|
||||||
|
|
||||||
|
match = Nor(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return len(s) == 5 },
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.ShouldBeFalse(match("0123456789"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPredicatesXnorPure(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestPredicatesXnorPure")
|
||||||
|
|
||||||
|
isEven := func(i int) bool { return i%2 == 0 }
|
||||||
|
isPositive := func(i int) bool { return i > 0 }
|
||||||
|
|
||||||
|
match := Xnor(isEven, isPositive)
|
||||||
|
|
||||||
|
assert.ShouldBeTrue(match(2))
|
||||||
|
assert.ShouldBeTrue(match(-3))
|
||||||
|
assert.ShouldBeFalse(match(3))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPredicatesMix(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestPredicatesMix")
|
||||||
|
|
||||||
|
a := Or(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "!") },
|
||||||
|
)
|
||||||
|
|
||||||
|
b := And(
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "hello") },
|
||||||
|
func(s string) bool { return strings.ContainsAny(s, "!") },
|
||||||
|
)
|
||||||
|
|
||||||
|
c := Negate(And(a, b))
|
||||||
|
|
||||||
|
assert.ShouldBeFalse(c("hello!"))
|
||||||
|
|
||||||
|
k := Nor(a, b)
|
||||||
|
assert.ShouldBeFalse(k("hello!"))
|
||||||
|
|
||||||
|
o := Xnor(a, b)
|
||||||
|
assert.ShouldBeTrue(o("hello!"))
|
||||||
|
|
||||||
|
p := Nand(c, k)
|
||||||
|
assert.ShouldBeTrue(p("hello!"))
|
||||||
|
}
|
||||||
@@ -37,6 +37,20 @@ func (a *Assert) Equal(expected, actual any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShouldBeFalse check if expected is false
|
||||||
|
func (a *Assert) ShouldBeFalse(actual any) {
|
||||||
|
if compare(false, actual) != compareEqual {
|
||||||
|
makeTestFailed(a.T, a.CaseName, false, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldBeTrue check if expected is true
|
||||||
|
func (a *Assert) ShouldBeTrue(actual any) {
|
||||||
|
if compare(true, actual) != compareEqual {
|
||||||
|
makeTestFailed(a.T, a.CaseName, true, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NotEqual check if expected is not equal with actual
|
// NotEqual check if expected is not equal with actual
|
||||||
func (a *Assert) NotEqual(expected, actual any) {
|
func (a *Assert) NotEqual(expected, actual any) {
|
||||||
if compare(expected, actual) == compareEqual {
|
if compare(expected, actual) == compareEqual {
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import (
|
|||||||
|
|
||||||
func TestAssert(t *testing.T) {
|
func TestAssert(t *testing.T) {
|
||||||
assert := NewAssert(t, "TestAssert")
|
assert := NewAssert(t, "TestAssert")
|
||||||
|
|
||||||
assert.Equal(0, 0)
|
assert.Equal(0, 0)
|
||||||
assert.NotEqual(1, 0)
|
assert.NotEqual(1, 0)
|
||||||
|
|
||||||
assert.NotEqual("1", 1)
|
assert.NotEqual("1", 1)
|
||||||
|
|
||||||
var uInt1 uint
|
var uInt1 uint
|
||||||
var uInt2 uint
|
var uInt2 uint
|
||||||
var uInt8 uint8
|
var uInt8 uint8
|
||||||
@@ -47,4 +48,11 @@ func TestAssert(t *testing.T) {
|
|||||||
assert.IsNil(nil)
|
assert.IsNil(nil)
|
||||||
assert.IsNotNil("abc")
|
assert.IsNotNil("abc")
|
||||||
|
|
||||||
|
var valA int = 1
|
||||||
|
var valB int64 = 1
|
||||||
|
assert.NotEqual(valA, valB)
|
||||||
|
assert.EqualValues(valA, valB)
|
||||||
|
|
||||||
|
assert.ShouldBeFalse(false)
|
||||||
|
assert.ShouldBeTrue(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,15 @@ type Iterator[T any] interface {
|
|||||||
Next() (item T, ok bool)
|
Next() (item T, ok bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopIterator is an interface for stopping Iterator.
|
// ResettableIterator supports to reset the iterator
|
||||||
|
type ResettableIterator[T any] interface {
|
||||||
|
Iterator[T]
|
||||||
|
// Reset allows for the iteration process over a sequence to be restarted from the beginning.
|
||||||
|
// It enables reusing the iterator for multiple traversals without needing to recreate it.
|
||||||
|
Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopIterator is an interface for stopping Iterator.
|
||||||
type StopIterator[T any] interface {
|
type StopIterator[T any] interface {
|
||||||
Iterator[T]
|
Iterator[T]
|
||||||
|
|
||||||
@@ -81,8 +89,8 @@ type PrevIterator[T any] interface {
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// FromSlice returns an iterator over a slice of data.
|
// FromSlice returns an iterator over a slice of data.
|
||||||
func FromSlice[T any](slice []T) Iterator[T] {
|
func FromSlice[T any](slice []T) *SliceIterator[T] {
|
||||||
return &sliceIterator[T]{slice: slice, index: -1}
|
return &SliceIterator[T]{slice: slice, index: -1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToSlice[T any](iter Iterator[T]) []T {
|
func ToSlice[T any](iter Iterator[T]) []T {
|
||||||
@@ -93,16 +101,16 @@ func ToSlice[T any](iter Iterator[T]) []T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
type sliceIterator[T any] struct {
|
type SliceIterator[T any] struct {
|
||||||
slice []T
|
slice []T
|
||||||
index int
|
index int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *sliceIterator[T]) HasNext() bool {
|
func (iter *SliceIterator[T]) HasNext() bool {
|
||||||
return iter.index < len(iter.slice)-1
|
return iter.index < len(iter.slice)-1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *sliceIterator[T]) Next() (T, bool) {
|
func (iter *SliceIterator[T]) Next() (T, bool) {
|
||||||
iter.index++
|
iter.index++
|
||||||
|
|
||||||
ok := iter.index >= 0 && iter.index < len(iter.slice)
|
ok := iter.index >= 0 && iter.index < len(iter.slice)
|
||||||
@@ -116,7 +124,7 @@ func (iter *sliceIterator[T]) Next() (T, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prev implements PrevIterator.
|
// Prev implements PrevIterator.
|
||||||
func (iter *sliceIterator[T]) Prev() {
|
func (iter *SliceIterator[T]) Prev() {
|
||||||
if iter.index == -1 {
|
if iter.index == -1 {
|
||||||
panic("Next function should be called Prev")
|
panic("Next function should be called Prev")
|
||||||
}
|
}
|
||||||
@@ -128,7 +136,7 @@ func (iter *sliceIterator[T]) Prev() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set implements SetIterator.
|
// Set implements SetIterator.
|
||||||
func (iter *sliceIterator[T]) Set(value T) {
|
func (iter *SliceIterator[T]) Set(value T) {
|
||||||
if iter.index == -1 {
|
if iter.index == -1 {
|
||||||
panic("Next function should be called Set")
|
panic("Next function should be called Set")
|
||||||
}
|
}
|
||||||
@@ -138,52 +146,60 @@ func (iter *sliceIterator[T]) Set(value T) {
|
|||||||
iter.slice[iter.index] = value
|
iter.slice[iter.index] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (iter *SliceIterator[T]) Reset() {
|
||||||
|
iter.index = -1
|
||||||
|
}
|
||||||
|
|
||||||
// FromRange creates a iterator which returns the numeric range between start inclusive and end
|
// FromRange creates a iterator which returns the numeric range between start inclusive and end
|
||||||
// exclusive by the step size. start should be less than end, step shoud be positive.
|
// exclusive by the step size. start should be less than end, step shoud be positive.
|
||||||
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) Iterator[T] {
|
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) *RangeIterator[T] {
|
||||||
if end < start {
|
if end < start {
|
||||||
panic("RangeIterator: start should be before end")
|
panic("RangeIterator: start should be before end")
|
||||||
} else if step <= 0 {
|
} else if step <= 0 {
|
||||||
panic("RangeIterator: step should be positive")
|
panic("RangeIterator: step should be positive")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &rangeIterator[T]{start: start, end: end, step: step}
|
return &RangeIterator[T]{start: start, end: end, step: step, current: start}
|
||||||
}
|
}
|
||||||
|
|
||||||
type rangeIterator[T constraints.Integer | constraints.Float] struct {
|
type RangeIterator[T constraints.Integer | constraints.Float] struct {
|
||||||
start, end, step T
|
start, end, step, current T
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *rangeIterator[T]) HasNext() bool {
|
func (iter *RangeIterator[T]) HasNext() bool {
|
||||||
return iter.start < iter.end
|
return iter.current < iter.end
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *rangeIterator[T]) Next() (T, bool) {
|
func (iter *RangeIterator[T]) Next() (T, bool) {
|
||||||
if iter.start >= iter.end {
|
if iter.current >= iter.end {
|
||||||
var zero T
|
var zero T
|
||||||
return zero, false
|
return zero, false
|
||||||
}
|
}
|
||||||
num := iter.start
|
num := iter.current
|
||||||
iter.start += iter.step
|
iter.current += iter.step
|
||||||
return num, true
|
return num, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromRange creates a iterator which returns the numeric range between start inclusive and end
|
func (iter *RangeIterator[T]) Reset() {
|
||||||
// exclusive by the step size. start should be less than end, step shoud be positive.
|
iter.current = iter.start
|
||||||
func FromChannel[T any](channel <-chan T) Iterator[T] {
|
|
||||||
return &channelIterator[T]{channel: channel}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type channelIterator[T any] struct {
|
// FromChannel creates an iterator which returns items received from the provided channel.
|
||||||
|
// The iteration continues until the channel is closed.
|
||||||
|
func FromChannel[T any](channel <-chan T) *ChannelIterator[T] {
|
||||||
|
return &ChannelIterator[T]{channel: channel}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelIterator[T any] struct {
|
||||||
channel <-chan T
|
channel <-chan T
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *channelIterator[T]) Next() (T, bool) {
|
func (iter *ChannelIterator[T]) Next() (T, bool) {
|
||||||
item, ok := <-iter.channel
|
item, ok := <-iter.channel
|
||||||
return item, ok
|
return item, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *channelIterator[T]) HasNext() bool {
|
func (iter *ChannelIterator[T]) HasNext() bool {
|
||||||
return len(iter.channel) == 0
|
return len(iter.channel) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,15 +50,36 @@ func TestSliceIterator(t *testing.T) {
|
|||||||
assert.Equal(false, ok)
|
assert.Equal(false, ok)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
t.Run("slice iterator Reset: ", func(t *testing.T) {
|
||||||
|
iter1 := FromSlice([]int{1, 2, 3, 4})
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
item, ok := iter1.Next()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
assert.Equal(i+1, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
iter1.Reset()
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
item, ok := iter1.Next()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
assert.Equal(i+1, item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("slice iterator ToSlice: ", func(t *testing.T) {
|
t.Run("slice iterator ToSlice: ", func(t *testing.T) {
|
||||||
iter := FromSlice([]int{1, 2, 3, 4})
|
iter := FromSlice([]int{1, 2, 3, 4})
|
||||||
item, _ := iter.Next()
|
item, _ := iter.Next()
|
||||||
assert.Equal(1, item)
|
assert.Equal(1, item)
|
||||||
|
|
||||||
data := ToSlice(iter)
|
data := ToSlice[int](iter)
|
||||||
assert.Equal([]int{2, 3, 4}, data)
|
assert.Equal([]int{2, 3, 4}, data)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRangeIterator(t *testing.T) {
|
func TestRangeIterator(t *testing.T) {
|
||||||
@@ -84,6 +105,54 @@ func TestRangeIterator(t *testing.T) {
|
|||||||
_, ok = iter.Next()
|
_, ok = iter.Next()
|
||||||
assert.Equal(false, ok)
|
assert.Equal(false, ok)
|
||||||
assert.Equal(false, iter.HasNext())
|
assert.Equal(false, iter.HasNext())
|
||||||
|
|
||||||
|
iter.Reset()
|
||||||
|
|
||||||
|
item, ok = iter.Next()
|
||||||
|
assert.Equal(1, item)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
item, ok = iter.Next()
|
||||||
|
assert.Equal(2, item)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
item, ok = iter.Next()
|
||||||
|
assert.Equal(3, item)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
_, ok = iter.Next()
|
||||||
|
assert.Equal(false, ok)
|
||||||
|
assert.Equal(false, iter.HasNext())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("range iterator reset: ", func(t *testing.T) {
|
||||||
|
iter := FromRange(1, 4, 1)
|
||||||
|
|
||||||
|
item, ok := iter.Next()
|
||||||
|
assert.Equal(1, item)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
item, ok = iter.Next()
|
||||||
|
assert.Equal(2, item)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
iter.Reset()
|
||||||
|
|
||||||
|
item, ok = iter.Next()
|
||||||
|
assert.Equal(1, item)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
item, ok = iter.Next()
|
||||||
|
assert.Equal(2, item)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
item, ok = iter.Next()
|
||||||
|
assert.Equal(3, item)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
_, ok = iter.Next()
|
||||||
|
assert.Equal(false, ok)
|
||||||
|
assert.Equal(false, iter.HasNext())
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -93,7 +162,7 @@ func TestChannelIterator(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestRangeIterator")
|
assert := internal.NewAssert(t, "TestRangeIterator")
|
||||||
|
|
||||||
iter := FromSlice([]int{1, 2, 3, 4})
|
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
iter = FromChannel(ToChannel(ctx, iter, 0))
|
iter = FromChannel(ToChannel(ctx, iter, 0))
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func TestMapIterator(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestMapIterator")
|
assert := internal.NewAssert(t, "TestMapIterator")
|
||||||
|
|
||||||
iter := FromSlice([]int{1, 2, 3, 4})
|
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
|
||||||
|
|
||||||
iter = Map(iter, func(n int) int { return n / 2 })
|
iter = Map(iter, func(n int) int { return n / 2 })
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ func TestFilterIterator(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestFilterIterator")
|
assert := internal.NewAssert(t, "TestFilterIterator")
|
||||||
|
|
||||||
iter := FromSlice([]int{1, 2, 3, 4})
|
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
|
||||||
|
|
||||||
iter = Filter(iter, func(n int) bool { return n < 3 })
|
iter = Filter(iter, func(n int) bool { return n < 3 })
|
||||||
|
|
||||||
@@ -47,10 +47,10 @@ func TestJoinIterator(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestJoinIterator")
|
assert := internal.NewAssert(t, "TestJoinIterator")
|
||||||
|
|
||||||
iter1 := FromSlice([]int{1, 2})
|
var iter1 Iterator[int] = FromSlice([]int{1, 2})
|
||||||
iter2 := FromSlice([]int{3, 4})
|
var iter2 Iterator[int] = FromSlice([]int{3, 4})
|
||||||
|
|
||||||
iter := Join(iter1, iter2)
|
var iter Iterator[int] = Join(iter1, iter2)
|
||||||
|
|
||||||
item, ok := iter.Next()
|
item, ok := iter.Next()
|
||||||
assert.Equal(1, item)
|
assert.Equal(1, item)
|
||||||
@@ -64,7 +64,7 @@ func TestReduce(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestReduce")
|
assert := internal.NewAssert(t, "TestReduce")
|
||||||
|
|
||||||
iter := FromSlice([]int{1, 2, 3, 4})
|
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
|
||||||
sum := Reduce(iter, 0, func(a, b int) int { return a + b })
|
sum := Reduce(iter, 0, func(a, b int) int { return a + b })
|
||||||
assert.Equal(10, sum)
|
assert.Equal(10, sum)
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ func TestTakeIterator(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestTakeIterator")
|
assert := internal.NewAssert(t, "TestTakeIterator")
|
||||||
|
|
||||||
iter := FromSlice([]int{1, 2, 3, 4, 5})
|
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4, 5})
|
||||||
|
|
||||||
iter = Take(iter, 3)
|
iter = Take(iter, 3)
|
||||||
|
|
||||||
|
|||||||
145
maputil/map.go
145
maputil/map.go
@@ -5,7 +5,12 @@
|
|||||||
package maputil
|
package maputil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/slice"
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
)
|
)
|
||||||
@@ -303,3 +308,143 @@ func HasKey[K comparable, V any](m map[K]V, key K) bool {
|
|||||||
_, haskey := m[key]
|
_, haskey := m[key]
|
||||||
return haskey
|
return haskey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapToStruct converts map to struct
|
||||||
|
// Play: https://go.dev/play/p/7wYyVfX38Dp
|
||||||
|
func MapToStruct(m map[string]any, structObj any) error {
|
||||||
|
for k, v := range m {
|
||||||
|
err := setStructField(structObj, k, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setStructField(structObj any, fieldName string, fieldValue any) error {
|
||||||
|
structVal := reflect.ValueOf(structObj).Elem()
|
||||||
|
|
||||||
|
fName := getFieldNameByJsonTag(structObj, fieldName)
|
||||||
|
if fName == "" {
|
||||||
|
return fmt.Errorf("Struct field json tag don't match map key : %s in obj", fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldVal := structVal.FieldByName(fName)
|
||||||
|
|
||||||
|
if !fieldVal.IsValid() {
|
||||||
|
return fmt.Errorf("No such field: %s in obj", fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fieldVal.CanSet() {
|
||||||
|
return fmt.Errorf("Cannot set %s field value", fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(fieldValue)
|
||||||
|
|
||||||
|
if fieldVal.Type() != val.Type() {
|
||||||
|
|
||||||
|
if val.CanConvert(fieldVal.Type()) {
|
||||||
|
fieldVal.Set(val.Convert(fieldVal.Type()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if m, ok := fieldValue.(map[string]any); ok {
|
||||||
|
|
||||||
|
if fieldVal.Kind() == reflect.Struct {
|
||||||
|
return MapToStruct(m, fieldVal.Addr().Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
if fieldVal.Kind() == reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct {
|
||||||
|
if fieldVal.IsNil() {
|
||||||
|
fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return MapToStruct(m, fieldVal.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Map value type don't match struct field type")
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldVal.Set(val)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFieldNameByJsonTag(structObj any, jsonTag string) string {
|
||||||
|
s := reflect.TypeOf(structObj).Elem()
|
||||||
|
|
||||||
|
for i := 0; i < s.NumField(); i++ {
|
||||||
|
field := s.Field(i)
|
||||||
|
tag := field.Tag
|
||||||
|
name, _, _ := strings.Cut(tag.Get("json"), ",")
|
||||||
|
if name == jsonTag {
|
||||||
|
return field.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSortedSlicesDefault converts a map to two slices sorted by key: one for the keys and another for the values.
|
||||||
|
// Play: https://go.dev/play/p/43gEM2po-qy
|
||||||
|
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V) {
|
||||||
|
keys := make([]K, 0, len(m))
|
||||||
|
|
||||||
|
// store the map’s keys into a slice
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the slice of keys
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
return keys[i] < keys[j]
|
||||||
|
})
|
||||||
|
|
||||||
|
// adjust the order of values according to the sorted keys
|
||||||
|
sortedValues := make([]V, len(keys))
|
||||||
|
for i, k := range keys {
|
||||||
|
sortedValues[i] = m[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys, sortedValues
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSortedSlicesWithComparator converts a map to two slices sorted by key and using a custom comparison function:
|
||||||
|
// one for the keys and another for the values.
|
||||||
|
// Play: https://go.dev/play/p/0nlPo6YLdt3
|
||||||
|
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V) {
|
||||||
|
keys := make([]K, 0, len(m))
|
||||||
|
|
||||||
|
// store the map’s keys into a slice
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the key slice using the provided comparison function
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
return comparator(keys[i], keys[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
// adjust the order of values according to the sorted keys
|
||||||
|
sortedValues := make([]V, len(keys))
|
||||||
|
for i, k := range keys {
|
||||||
|
sortedValues[i] = m[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys, sortedValues
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrSet returns value of the given key or set the given value value if not present.
|
||||||
|
// Play: todo
|
||||||
|
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V {
|
||||||
|
if v, ok := m[key]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
m[key] = value
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleKeys() {
|
func ExampleKeys() {
|
||||||
@@ -450,3 +451,92 @@ func ExampleHasKey() {
|
|||||||
// true
|
// true
|
||||||
// false
|
// false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleMapToStruct() {
|
||||||
|
|
||||||
|
personReqMap := map[string]any{
|
||||||
|
"name": "Nothin",
|
||||||
|
"max_age": 35,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersonReq struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MaxAge int `json:"max_age"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
|
}
|
||||||
|
var personReq PersonReq
|
||||||
|
_ = MapToStruct(personReqMap, &personReq)
|
||||||
|
fmt.Println(personReq)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {Nothin 35 1 10}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleToSortedSlicesDefault() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, values := ToSortedSlicesDefault(m)
|
||||||
|
|
||||||
|
fmt.Println(keys)
|
||||||
|
fmt.Println(values)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2 3]
|
||||||
|
// [a b c]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleToSortedSlicesWithComparator() {
|
||||||
|
m1 := map[time.Time]string{
|
||||||
|
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
|
||||||
|
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
|
||||||
|
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys1, values1 := ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
|
||||||
|
return a.Before(b)
|
||||||
|
})
|
||||||
|
|
||||||
|
m2 := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
keys2, values2 := ToSortedSlicesWithComparator(m2, func(a, b int) bool {
|
||||||
|
return a > b
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(keys1)
|
||||||
|
fmt.Println(values1)
|
||||||
|
|
||||||
|
fmt.Println(keys2)
|
||||||
|
fmt.Println(values2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [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]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleGetOrSet() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
}
|
||||||
|
|
||||||
|
result1 := GetOrSet(m, 1, "1")
|
||||||
|
result2 := GetOrSet(m, 2, "b")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// a
|
||||||
|
// b
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package maputil
|
package maputil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/cmplx"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
)
|
)
|
||||||
@@ -472,3 +474,236 @@ func TestHasKey(t *testing.T) {
|
|||||||
assert.Equal(true, HasKey(m, "a"))
|
assert.Equal(true, HasKey(m, "a"))
|
||||||
assert.Equal(false, HasKey(m, "c"))
|
assert.Equal(false, HasKey(m, "c"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapToStruct(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestMapToStruct")
|
||||||
|
|
||||||
|
type (
|
||||||
|
Person struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Age int `json:"age"`
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
Addr *Address `json:"address,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Address struct {
|
||||||
|
Street string `json:"street"`
|
||||||
|
Number int `json:"number"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
m := map[string]interface{}{
|
||||||
|
"name": "Nothin",
|
||||||
|
"age": 28,
|
||||||
|
"phone": "123456789",
|
||||||
|
"address": map[string]interface{}{
|
||||||
|
"street": "test",
|
||||||
|
"number": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var p Person
|
||||||
|
err := MapToStruct(m, &p)
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(m["name"], p.Name)
|
||||||
|
assert.Equal(m["age"], p.Age)
|
||||||
|
assert.Equal(m["phone"], p.Phone)
|
||||||
|
assert.Equal("test", p.Addr.Street)
|
||||||
|
assert.Equal(1, p.Addr.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToSortedSlicesDefault(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestToSortedSlicesDefault")
|
||||||
|
|
||||||
|
testCases1 := []struct {
|
||||||
|
name string
|
||||||
|
inputMap map[string]any
|
||||||
|
expKeys []string
|
||||||
|
expValues []any
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Empty Map",
|
||||||
|
inputMap: map[string]any{},
|
||||||
|
expKeys: []string{},
|
||||||
|
expValues: []any{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Unsorted Map",
|
||||||
|
inputMap: map[string]any{"c": 3, "a": 1.6, "b": 2},
|
||||||
|
expKeys: []string{"a", "b", "c"},
|
||||||
|
expValues: []any{1.6, 2, 3},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases1 {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
keys, values := ToSortedSlicesDefault(tc.inputMap)
|
||||||
|
assert.Equal(tc.expKeys, keys)
|
||||||
|
assert.Equal(tc.expValues, values)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases2 := map[int64]string{
|
||||||
|
7: "seven",
|
||||||
|
3: "three",
|
||||||
|
5: "five",
|
||||||
|
}
|
||||||
|
actualK2, actualV2 := ToSortedSlicesDefault(testCases2)
|
||||||
|
case2Keys := []int64{3, 5, 7}
|
||||||
|
case2Values := []string{"three", "five", "seven"}
|
||||||
|
assert.Equal(case2Keys, actualK2)
|
||||||
|
assert.Equal(case2Values, actualV2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToSortedSlicesWithComparator(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestToSortedSlicesWithComparator")
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
ID int
|
||||||
|
Name string
|
||||||
|
CreateTime time.Time
|
||||||
|
FavorComplexNumber complex128
|
||||||
|
Signal chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
name string
|
||||||
|
inputMap map[Account]any
|
||||||
|
lessFunc func(a, b Account) bool
|
||||||
|
expKeys []Account
|
||||||
|
expValues []any
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
tomorrow := now.AddDate(0, 0, 1)
|
||||||
|
yesterday := now.AddDate(0, 0, -1)
|
||||||
|
|
||||||
|
account1 := Account{ID: 1, Name: "cya", CreateTime: now, FavorComplexNumber: complex(1.2, 3),
|
||||||
|
Signal: make(chan struct{}, 10)}
|
||||||
|
account2 := Account{ID: 2, Name: "Cannian", CreateTime: tomorrow, FavorComplexNumber: complex(1.2, 2),
|
||||||
|
Signal: make(chan struct{}, 7)}
|
||||||
|
account3 := Account{ID: 3, Name: "Clauviou", CreateTime: yesterday, FavorComplexNumber: complex(3, 4),
|
||||||
|
Signal: make(chan struct{}, 3)}
|
||||||
|
account1.Signal <- struct{}{}
|
||||||
|
account2.Signal <- struct{}{}
|
||||||
|
account2.Signal <- struct{}{}
|
||||||
|
|
||||||
|
testCases := []testCase{
|
||||||
|
{
|
||||||
|
name: "Sorted by Account ID",
|
||||||
|
inputMap: map[Account]any{
|
||||||
|
account1: 100,
|
||||||
|
account2: 200,
|
||||||
|
account3: 300,
|
||||||
|
},
|
||||||
|
lessFunc: func(a, b Account) bool { return a.ID < b.ID },
|
||||||
|
expKeys: []Account{account1, account2, account3},
|
||||||
|
expValues: []any{100, 200, 300},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Reverse Sorted by Account ID",
|
||||||
|
inputMap: map[Account]any{
|
||||||
|
account1: 100,
|
||||||
|
account2: 200,
|
||||||
|
account3: 300,
|
||||||
|
},
|
||||||
|
lessFunc: func(a, b Account) bool { return a.ID > b.ID },
|
||||||
|
expKeys: []Account{account3, account2, account1},
|
||||||
|
expValues: []any{300, 200, 100},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Sorted by Account Name",
|
||||||
|
inputMap: map[Account]any{
|
||||||
|
account1: 100,
|
||||||
|
account2: 200,
|
||||||
|
account3: 300,
|
||||||
|
},
|
||||||
|
lessFunc: func(a, b Account) bool { return a.Name < b.Name },
|
||||||
|
expKeys: []Account{account2, account3, account1},
|
||||||
|
expValues: []any{200, 300, 100},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty Map",
|
||||||
|
inputMap: map[Account]any{},
|
||||||
|
lessFunc: func(a, b Account) bool { return a.ID < b.ID },
|
||||||
|
expKeys: []Account{},
|
||||||
|
expValues: []any{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Sorted by Account CreateTime",
|
||||||
|
inputMap: map[Account]any{
|
||||||
|
account1: "now",
|
||||||
|
account2: "tomorrow",
|
||||||
|
account3: "yesterday",
|
||||||
|
},
|
||||||
|
lessFunc: func(a, b Account) bool { return a.CreateTime.Before(b.CreateTime) },
|
||||||
|
expKeys: []Account{account3, account1, account2},
|
||||||
|
expValues: []any{"yesterday", "now", "tomorrow"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Sorted by Account FavorComplexNumber",
|
||||||
|
inputMap: map[Account]any{
|
||||||
|
account1: "1.2+3i",
|
||||||
|
account2: "1.2+2i",
|
||||||
|
account3: "3+4i",
|
||||||
|
},
|
||||||
|
lessFunc: func(a, b Account) bool { return cmplx.Abs(a.FavorComplexNumber) < cmplx.Abs(b.FavorComplexNumber) },
|
||||||
|
expKeys: []Account{account2, account1, account3},
|
||||||
|
expValues: []any{"1.2+2i", "1.2+3i", "3+4i"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Sort by the buffer capacity of the channel",
|
||||||
|
inputMap: map[Account]any{
|
||||||
|
account1: 10,
|
||||||
|
account2: 7,
|
||||||
|
account3: 3,
|
||||||
|
},
|
||||||
|
lessFunc: func(a, b Account) bool {
|
||||||
|
return cap(a.Signal) < cap(b.Signal)
|
||||||
|
},
|
||||||
|
expKeys: []Account{account3, account2, account1},
|
||||||
|
expValues: []any{3, 7, 10},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Sort by the number of elements in the channel",
|
||||||
|
inputMap: map[Account]any{
|
||||||
|
account1: 1,
|
||||||
|
account2: 2,
|
||||||
|
account3: 0,
|
||||||
|
},
|
||||||
|
lessFunc: func(a, b Account) bool { return len(a.Signal) < len(b.Signal) },
|
||||||
|
expKeys: []Account{account3, account1, account2},
|
||||||
|
expValues: []any{0, 1, 2},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
keys, values := ToSortedSlicesWithComparator(tc.inputMap, tc.lessFunc)
|
||||||
|
assert.Equal(tc.expKeys, keys)
|
||||||
|
assert.Equal(tc.expValues, values)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOrSet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestGetOrSet")
|
||||||
|
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
}
|
||||||
|
|
||||||
|
result1 := GetOrSet(m, 1, "1")
|
||||||
|
result2 := GetOrSet(m, 2, "b")
|
||||||
|
|
||||||
|
assert.Equal("a", result1)
|
||||||
|
assert.Equal("b", result2)
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,18 +62,25 @@ var _ = func() struct{} {
|
|||||||
*/
|
*/
|
||||||
// Play: https://go.dev/play/p/4K7KBEPgS5M
|
// Play: https://go.dev/play/p/4K7KBEPgS5M
|
||||||
func MapTo(src any, dst any) error {
|
func MapTo(src any, dst any) error {
|
||||||
|
|
||||||
dstRef := reflect.ValueOf(dst)
|
dstRef := reflect.ValueOf(dst)
|
||||||
|
|
||||||
if dstRef.Kind() != reflect.Ptr {
|
if dstRef.Kind() != reflect.Ptr {
|
||||||
return fmt.Errorf("dst is not 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)
|
dstRef = reflect.Indirect(dstRef)
|
||||||
|
|
||||||
srcRef := reflect.ValueOf(src)
|
srcRef := reflect.ValueOf(src)
|
||||||
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
|
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
|
||||||
srcRef = srcRef.Elem()
|
srcRef = srcRef.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
if f, ok := mapHandlers[srcRef.Kind()]; ok {
|
if f, ok := mapHandlers[srcRef.Kind()]; ok {
|
||||||
return f(srcRef, dstRef)
|
return f(srcRef, dstRef)
|
||||||
}
|
}
|
||||||
@@ -121,7 +128,7 @@ func convertSlice(src reflect.Value, dst reflect.Value) error {
|
|||||||
|
|
||||||
func convertMap(src reflect.Value, dst reflect.Value) error {
|
func convertMap(src reflect.Value, dst reflect.Value) error {
|
||||||
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
|
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
|
||||||
if src.Kind() == reflect.Interface {
|
if src.Kind() == reflect.Interface && dst.IsValid() {
|
||||||
return convertMap(src.Elem(), dst)
|
return convertMap(src.Elem(), dst)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
|
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
|
||||||
@@ -129,7 +136,9 @@ func convertMap(src reflect.Value, dst reflect.Value) error {
|
|||||||
}
|
}
|
||||||
dstType := dst.Type()
|
dstType := dst.Type()
|
||||||
num := dstType.NumField()
|
num := dstType.NumField()
|
||||||
|
|
||||||
exist := map[string]int{}
|
exist := map[string]int{}
|
||||||
|
|
||||||
for i := 0; i < num; i++ {
|
for i := 0; i < num; i++ {
|
||||||
k := dstType.Field(i).Tag.Get("json")
|
k := dstType.Field(i).Tag.Get("json")
|
||||||
if k == "" {
|
if k == "" {
|
||||||
@@ -138,7 +147,6 @@ func convertMap(src reflect.Value, dst reflect.Value) error {
|
|||||||
if strings.Contains(k, ",") {
|
if strings.Contains(k, ",") {
|
||||||
taglist := strings.Split(k, ",")
|
taglist := strings.Split(k, ",")
|
||||||
if taglist[0] == "" {
|
if taglist[0] == "" {
|
||||||
|
|
||||||
k = dstType.Field(i).Name
|
k = dstType.Field(i).Name
|
||||||
} else {
|
} else {
|
||||||
k = taglist[0]
|
k = taglist[0]
|
||||||
@@ -150,9 +158,11 @@ func convertMap(src reflect.Value, dst reflect.Value) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
keys := src.MapKeys()
|
keys := src.MapKeys()
|
||||||
|
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
if index, ok := exist[key.String()]; ok {
|
if index, ok := exist[key.String()]; ok {
|
||||||
v := dst.Field(index)
|
v := dst.Field(index)
|
||||||
|
|
||||||
if v.Kind() == reflect.Struct {
|
if v.Kind() == reflect.Struct {
|
||||||
err := convertMap(src.MapIndex(key), v)
|
err := convertMap(src.MapIndex(key), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
Person struct {
|
Person struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Age int `json:"age"`
|
Age int `json:"age"`
|
||||||
Phone string `json:"phone"`
|
Phone string `json:"phone"`
|
||||||
Addr Address `json:"address"`
|
Address *Address `json:"address"`
|
||||||
}
|
}
|
||||||
|
|
||||||
Address struct {
|
Address struct {
|
||||||
@@ -38,11 +38,12 @@ func TestStructType(t *testing.T) {
|
|||||||
var p Person
|
var p Person
|
||||||
err := MapTo(src, &p)
|
err := MapTo(src, &p)
|
||||||
assert.IsNil(err)
|
assert.IsNil(err)
|
||||||
|
|
||||||
assert.Equal(src["name"], p.Name)
|
assert.Equal(src["name"], p.Name)
|
||||||
assert.Equal(src["age"], p.Age)
|
assert.Equal(src["age"], p.Age)
|
||||||
assert.Equal(src["phone"], p.Phone)
|
assert.Equal(src["phone"], p.Phone)
|
||||||
assert.Equal("test", p.Addr.Street)
|
assert.Equal("test", p.Address.Street)
|
||||||
assert.Equal(1, p.Addr.Number)
|
assert.Equal(1, p.Address.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseType(t *testing.T) {
|
func TestBaseType(t *testing.T) {
|
||||||
|
|||||||
@@ -66,28 +66,28 @@ func Percent(val, total float64, n int) float64 {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundToString round up to n decimal places.
|
// RoundToString round off to n decimal places.
|
||||||
// Play: https://go.dev/play/p/kZwpBRAcllO
|
// Play: https://go.dev/play/p/kZwpBRAcllO
|
||||||
func RoundToString(x float64, n int) string {
|
func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
||||||
tmp := math.Pow(10.0, float64(n))
|
tmp := math.Pow(10.0, float64(n))
|
||||||
x *= tmp
|
x *= T(tmp)
|
||||||
x = math.Round(x)
|
r := math.Round(float64(x))
|
||||||
result := strconv.FormatFloat(x/tmp, 'f', n, 64)
|
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundToFloat round up to n decimal places.
|
// RoundToFloat round off to n decimal places.
|
||||||
// Play: https://go.dev/play/p/ghyb528JRJL
|
// Play: https://go.dev/play/p/ghyb528JRJL
|
||||||
func RoundToFloat(x float64, n int) float64 {
|
func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
||||||
tmp := math.Pow(10.0, float64(n))
|
tmp := math.Pow(10.0, float64(n))
|
||||||
x *= tmp
|
x *= T(tmp)
|
||||||
x = math.Round(x)
|
r := math.Round(float64(x))
|
||||||
return x / tmp
|
return r / tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
// TruncRound round off n decimal places.
|
// TruncRound round off n decimal places.
|
||||||
// Play: https://go.dev/play/p/aumarSHIGzP
|
// Play: https://go.dev/play/p/aumarSHIGzP
|
||||||
func TruncRound(x float64, n int) float64 {
|
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T {
|
||||||
floatStr := fmt.Sprintf("%."+strconv.Itoa(n+1)+"f", x)
|
floatStr := fmt.Sprintf("%."+strconv.Itoa(n+1)+"f", x)
|
||||||
temp := strings.Split(floatStr, ".")
|
temp := strings.Split(floatStr, ".")
|
||||||
var newFloat string
|
var newFloat string
|
||||||
@@ -97,6 +97,44 @@ func TruncRound(x float64, n int) float64 {
|
|||||||
newFloat = temp[0] + "." + temp[1][:n]
|
newFloat = temp[0] + "." + temp[1][:n]
|
||||||
}
|
}
|
||||||
result, _ := strconv.ParseFloat(newFloat, 64)
|
result, _ := strconv.ParseFloat(newFloat, 64)
|
||||||
|
return T(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloorToFloat round down to n decimal places.
|
||||||
|
// Play: https://go.dev/play/p/vbCBrQHZEED
|
||||||
|
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
||||||
|
tmp := math.Pow(10.0, float64(n))
|
||||||
|
x *= T(tmp)
|
||||||
|
r := math.Floor(float64(x))
|
||||||
|
return r / tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloorToString round down to n decimal places.
|
||||||
|
// Play: https://go.dev/play/p/Qk9KPd2IdDb
|
||||||
|
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
||||||
|
tmp := math.Pow(10.0, float64(n))
|
||||||
|
x *= T(tmp)
|
||||||
|
r := math.Floor(float64(x))
|
||||||
|
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// CeilToFloat round up to n decimal places.
|
||||||
|
// Play: https://go.dev/play/p/8hOeSADZPCo
|
||||||
|
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
||||||
|
tmp := math.Pow(10.0, float64(n))
|
||||||
|
x *= T(tmp)
|
||||||
|
r := math.Ceil(float64(x))
|
||||||
|
return r / tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
// CeilToString round up to n decimal places.
|
||||||
|
// Play: https://go.dev/play/p/wy5bYEyUKKG
|
||||||
|
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
||||||
|
tmp := math.Pow(10.0, float64(n))
|
||||||
|
x *= T(tmp)
|
||||||
|
r := math.Ceil(float64(x))
|
||||||
|
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +291,7 @@ func PointDistance(x1, y1, x2, y2 float64) float64 {
|
|||||||
return math.Sqrt(c)
|
return math.Sqrt(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPrimes checks if number is prime number.
|
// IsPrime checks if number is prime number.
|
||||||
// Play: https://go.dev/play/p/Rdd8UTHZJ7u
|
// Play: https://go.dev/play/p/Rdd8UTHZJ7u
|
||||||
func IsPrime(n int) bool {
|
func IsPrime(n int) bool {
|
||||||
if n < 2 {
|
if n < 2 {
|
||||||
@@ -351,3 +389,9 @@ func Abs[T constraints.Integer | constraints.Float](x T) T {
|
|||||||
|
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Div returns the result of x divided by y.
|
||||||
|
// Play: https://go.dev/play/p/WLxDdGXXYat
|
||||||
|
func Div[T constraints.Float | constraints.Integer](x T, y T) float64 {
|
||||||
|
return float64(x) / float64(y)
|
||||||
|
}
|
||||||
|
|||||||
@@ -110,6 +110,66 @@ func ExampleTruncRound() {
|
|||||||
// 0.125
|
// 0.125
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleCeilToFloat() {
|
||||||
|
result1 := CeilToFloat(3.14159, 1)
|
||||||
|
result2 := CeilToFloat(3.14159, 2)
|
||||||
|
result3 := CeilToFloat(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.2
|
||||||
|
// 3.15
|
||||||
|
// 5
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleCeilToString() {
|
||||||
|
result1 := CeilToString(3.14159, 1)
|
||||||
|
result2 := CeilToString(3.14159, 2)
|
||||||
|
result3 := CeilToString(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.2
|
||||||
|
// 3.15
|
||||||
|
// 5.0000
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFloorToFloat() {
|
||||||
|
result1 := FloorToFloat(3.14159, 1)
|
||||||
|
result2 := FloorToFloat(3.14159, 2)
|
||||||
|
result3 := FloorToFloat(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.1
|
||||||
|
// 3.14
|
||||||
|
// 5
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleFloorToString() {
|
||||||
|
result1 := FloorToString(3.14159, 1)
|
||||||
|
result2 := FloorToString(3.14159, 2)
|
||||||
|
result3 := FloorToString(5, 4)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3.1
|
||||||
|
// 3.14
|
||||||
|
// 5.0000
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleAverage() {
|
func ExampleAverage() {
|
||||||
result1 := Average(1, 2)
|
result1 := Average(1, 2)
|
||||||
result2 := RoundToFloat(Average(1.2, 1.4), 1)
|
result2 := RoundToFloat(Average(1.2, 1.4), 1)
|
||||||
@@ -404,3 +464,17 @@ func ExampleAbs() {
|
|||||||
// 0.1
|
// 0.1
|
||||||
// 0.2
|
// 0.2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleDiv() {
|
||||||
|
result1 := Div(9, 4)
|
||||||
|
result2 := Div(1, 2)
|
||||||
|
result3 := Div(0, 666)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
// Output:
|
||||||
|
// 2.25
|
||||||
|
// 0.5
|
||||||
|
// 0
|
||||||
|
}
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ func TestRoundToString(t *testing.T) {
|
|||||||
assert.Equal("0.12", RoundToString(0.124, 2))
|
assert.Equal("0.12", RoundToString(0.124, 2))
|
||||||
assert.Equal("0.13", RoundToString(0.125, 2))
|
assert.Equal("0.13", RoundToString(0.125, 2))
|
||||||
assert.Equal("0.125", RoundToString(0.125, 3))
|
assert.Equal("0.125", RoundToString(0.125, 3))
|
||||||
|
assert.Equal("54.321", RoundToString(54.321, 3))
|
||||||
|
assert.Equal("17.000", RoundToString(17, 3))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTruncRound(t *testing.T) {
|
func TestTruncRound(t *testing.T) {
|
||||||
@@ -79,14 +81,63 @@ func TestTruncRound(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestTruncRound")
|
assert := internal.NewAssert(t, "TestTruncRound")
|
||||||
|
|
||||||
assert.Equal(float64(0), TruncRound(0, 0))
|
assert.Equal(float64(0), TruncRound(float64(0), 0))
|
||||||
assert.Equal(float64(0), TruncRound(0, 1))
|
assert.Equal(float64(0), TruncRound(float64(0), 1))
|
||||||
|
assert.Equal(float32(0), TruncRound(float32(0), 0))
|
||||||
|
assert.Equal(float32(0), TruncRound(float32(0), 1))
|
||||||
|
assert.Equal(0, TruncRound(0, 0))
|
||||||
|
assert.Equal(uint64(0), TruncRound(uint64(0), 1))
|
||||||
assert.Equal(0.12, TruncRound(0.124, 2))
|
assert.Equal(0.12, TruncRound(0.124, 2))
|
||||||
assert.Equal(0.12, TruncRound(0.125, 2))
|
assert.Equal(0.12, TruncRound(0.125, 2))
|
||||||
assert.Equal(0.125, TruncRound(0.125, 3))
|
assert.Equal(0.125, TruncRound(0.125, 3))
|
||||||
assert.Equal(33.33, TruncRound(33.33333, 2))
|
assert.Equal(33.33, TruncRound(33.33333, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFloorToFloat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestFloorToFloat")
|
||||||
|
|
||||||
|
assert.Equal(3.14, FloorToFloat(3.14159, 2))
|
||||||
|
assert.Equal(3.141, FloorToFloat(3.14159, 3))
|
||||||
|
assert.Equal(5.0, FloorToFloat(5, 4))
|
||||||
|
assert.Equal(2.0, FloorToFloat(9/4, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFloorToString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestFloorToString")
|
||||||
|
|
||||||
|
assert.Equal("3.14", FloorToString(3.14159, 2))
|
||||||
|
assert.Equal("3.141", FloorToString(3.14159, 3))
|
||||||
|
assert.Equal("5.0000", FloorToString(5, 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCeilToFloat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestCeilToFloat")
|
||||||
|
|
||||||
|
assert.Equal(3.15, CeilToFloat(3.14159, 2))
|
||||||
|
assert.Equal(3.142, CeilToFloat(3.14159, 3))
|
||||||
|
assert.Equal(5.0, CeilToFloat(5, 4))
|
||||||
|
assert.Equal(2.25, CeilToFloat(float32(9)/float32(4), 2))
|
||||||
|
assert.Equal(0.15, CeilToFloat(float64(1)/float64(7), 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCeilToString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestCeilToFloat")
|
||||||
|
|
||||||
|
assert.Equal("3.15", CeilToString(3.14159, 2))
|
||||||
|
assert.Equal("3.142", CeilToString(3.14159, 3))
|
||||||
|
assert.Equal("5.0000", CeilToString(5, 4))
|
||||||
|
assert.Equal("2.25", CeilToString(float32(9)/float32(4), 2))
|
||||||
|
assert.Equal("0.15", CeilToString(float64(1)/float64(7), 2))
|
||||||
|
}
|
||||||
|
|
||||||
func TestAverage(t *testing.T) {
|
func TestAverage(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -346,4 +397,19 @@ func TestAbs(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(int64(1), Abs(int64(-1)))
|
assert.Equal(int64(1), Abs(int64(-1)))
|
||||||
assert.Equal(float32(1), Abs(float32(-1)))
|
assert.Equal(float32(1), Abs(float32(-1)))
|
||||||
|
assert.Equal(math.Inf(1), Abs(math.Inf(-1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDiv(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestDiv")
|
||||||
|
|
||||||
|
assert.Equal(float64(2), Div(4, 2))
|
||||||
|
assert.Equal(2.5, Div(5, 2))
|
||||||
|
assert.Equal(0.5, Div(1, 2))
|
||||||
|
assert.Equal(0.5, Div(1, 2))
|
||||||
|
assert.Equal(math.Inf(1), Div(8, 0))
|
||||||
|
assert.Equal(math.Inf(-1), Div(-8, 0))
|
||||||
|
assert.Equal(true, math.IsNaN(Div(0, 0)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/convertor"
|
|
||||||
"github.com/duke-git/lancet/v2/slice"
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -103,19 +102,22 @@ type HttpRequest struct {
|
|||||||
|
|
||||||
// HttpClientConfig contains some configurations for http client
|
// HttpClientConfig contains some configurations for http client
|
||||||
type HttpClientConfig struct {
|
type HttpClientConfig struct {
|
||||||
|
Timeout time.Duration
|
||||||
SSLEnabled bool
|
SSLEnabled bool
|
||||||
TLSConfig *tls.Config
|
TLSConfig *tls.Config
|
||||||
Compressed bool
|
Compressed bool
|
||||||
HandshakeTimeout time.Duration
|
HandshakeTimeout time.Duration
|
||||||
ResponseTimeout time.Duration
|
ResponseTimeout time.Duration
|
||||||
Verbose bool
|
Verbose bool
|
||||||
|
Proxy *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultHttpClientConfig defalut client config.
|
// defaultHttpClientConfig defalut client config.
|
||||||
var defaultHttpClientConfig = &HttpClientConfig{
|
var defaultHttpClientConfig = &HttpClientConfig{
|
||||||
|
Timeout: 50 * time.Second,
|
||||||
Compressed: false,
|
Compressed: false,
|
||||||
HandshakeTimeout: 20 * time.Second,
|
HandshakeTimeout: 10 * time.Second,
|
||||||
ResponseTimeout: 40 * time.Second,
|
ResponseTimeout: 10 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
// HttpClient is used for sending http request.
|
// HttpClient is used for sending http request.
|
||||||
@@ -131,6 +133,7 @@ type HttpClient struct {
|
|||||||
func NewHttpClient() *HttpClient {
|
func NewHttpClient() *HttpClient {
|
||||||
client := &HttpClient{
|
client := &HttpClient{
|
||||||
Client: &http.Client{
|
Client: &http.Client{
|
||||||
|
Timeout: defaultHttpClientConfig.Timeout,
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSHandshakeTimeout: defaultHttpClientConfig.HandshakeTimeout,
|
TLSHandshakeTimeout: defaultHttpClientConfig.HandshakeTimeout,
|
||||||
ResponseHeaderTimeout: defaultHttpClientConfig.ResponseTimeout,
|
ResponseHeaderTimeout: defaultHttpClientConfig.ResponseTimeout,
|
||||||
@@ -164,6 +167,11 @@ func NewHttpClientWithConfig(config *HttpClientConfig) *HttpClient {
|
|||||||
client.TLS = config.TLSConfig
|
client.TLS = config.TLSConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.Proxy != nil {
|
||||||
|
transport := client.Client.Transport.(*http.Transport)
|
||||||
|
transport.Proxy = http.ProxyURL(config.Proxy)
|
||||||
|
}
|
||||||
|
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,11 +371,20 @@ func validateRequest(req *HttpRequest) error {
|
|||||||
// Play: https://go.dev/play/p/pFqMkM40w9z
|
// Play: https://go.dev/play/p/pFqMkM40w9z
|
||||||
func StructToUrlValues(targetStruct any) (url.Values, error) {
|
func StructToUrlValues(targetStruct any) (url.Values, error) {
|
||||||
result := url.Values{}
|
result := url.Values{}
|
||||||
s, err := convertor.StructToMap(targetStruct)
|
|
||||||
|
var m map[string]interface{}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(targetStruct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for k, v := range s {
|
|
||||||
|
err = json.Unmarshal(jsonBytes, &m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range m {
|
||||||
result.Add(k, fmt.Sprintf("%v", v))
|
result.Add(k, fmt.Sprintf("%v", v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
)
|
)
|
||||||
@@ -23,7 +23,8 @@ func TestHttpGet(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := HttpGet(url, header)
|
resp, err := HttpGet(url, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: " + err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
@@ -44,8 +45,10 @@ func TestHttpPost(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := HttpPost(url, header, nil, bodyParams)
|
resp, err := HttpPost(url, header, nil, bodyParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: " + err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
t.Log("response: ", resp.StatusCode, string(body))
|
t.Log("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
@@ -54,21 +57,18 @@ func TestHttpPostFormData(t *testing.T) {
|
|||||||
apiUrl := "https://jsonplaceholder.typicode.com/todos"
|
apiUrl := "https://jsonplaceholder.typicode.com/todos"
|
||||||
header := map[string]string{
|
header := map[string]string{
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
// "Content-Type": "multipart/form-data",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
postData := url.Values{}
|
postData := url.Values{}
|
||||||
postData.Add("userId", "1")
|
postData.Add("userId", "1")
|
||||||
postData.Add("title", "TestToDo")
|
postData.Add("title", "TestToDo")
|
||||||
|
|
||||||
// postData := make(map[string]string)
|
|
||||||
// postData["userId"] = "1"
|
|
||||||
// postData["title"] = "title"
|
|
||||||
|
|
||||||
resp, err := HttpPost(apiUrl, header, nil, postData)
|
resp, err := HttpPost(apiUrl, header, nil, postData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: " + err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
t.Log("response: ", resp.StatusCode, string(body))
|
t.Log("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
@@ -88,8 +88,10 @@ func TestHttpPut(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := HttpPut(url, header, nil, bodyParams)
|
resp, err := HttpPut(url, header, nil, bodyParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
t.Log("response: ", resp.StatusCode, string(body))
|
t.Log("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
@@ -109,8 +111,10 @@ func TestHttpPatch(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := HttpPatch(url, header, nil, bodyParams)
|
resp, err := HttpPatch(url, header, nil, bodyParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
t.Log("response: ", resp.StatusCode, string(body))
|
t.Log("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
@@ -119,8 +123,10 @@ func TestHttpDelete(t *testing.T) {
|
|||||||
url := "https://jsonplaceholder.typicode.com/todos/1"
|
url := "https://jsonplaceholder.typicode.com/todos/1"
|
||||||
resp, err := HttpDelete(url)
|
resp, err := HttpDelete(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
t.Log("response: ", resp.StatusCode, string(body))
|
t.Log("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
@@ -147,7 +153,8 @@ func TestParseResponse(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := HttpGet(url, header)
|
resp, err := HttpGet(url, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type Todo struct {
|
type Todo struct {
|
||||||
@@ -160,8 +167,10 @@ func TestParseResponse(t *testing.T) {
|
|||||||
toDoResp := &Todo{}
|
toDoResp := &Todo{}
|
||||||
err = ParseHttpResponse(resp, toDoResp)
|
err = ParseHttpResponse(resp, toDoResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("response: ", toDoResp)
|
t.Log("response: ", toDoResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +187,8 @@ func TestHttpClient_Get(t *testing.T) {
|
|||||||
httpClient := NewHttpClient()
|
httpClient := NewHttpClient()
|
||||||
resp, err := httpClient.SendRequest(request)
|
resp, err := httpClient.SendRequest(request)
|
||||||
if err != nil || resp.StatusCode != 200 {
|
if err != nil || resp.StatusCode != 200 {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type Todo struct {
|
type Todo struct {
|
||||||
@@ -215,7 +225,8 @@ func TestHttpClent_Post(t *testing.T) {
|
|||||||
httpClient := NewHttpClient()
|
httpClient := NewHttpClient()
|
||||||
resp, err := httpClient.SendRequest(request)
|
resp, err := httpClient.SendRequest(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
@@ -227,16 +238,25 @@ func TestStructToUrlValues(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestStructToUrlValues")
|
assert := internal.NewAssert(t, "TestStructToUrlValues")
|
||||||
|
|
||||||
|
type CommReq struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
type TodoQuery struct {
|
type TodoQuery struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
UserId int `json:"userId"`
|
UserId int `json:"userId"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
CommReq `json:",inline"`
|
||||||
}
|
}
|
||||||
item1 := TodoQuery{
|
item1 := TodoQuery{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
UserId: 123,
|
UserId: 123,
|
||||||
Name: "",
|
Name: "",
|
||||||
|
CommReq: CommReq{
|
||||||
|
Version: "1.0",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
todoValues, err := StructToUrlValues(item1)
|
todoValues, err := StructToUrlValues(item1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("params is invalid: %v", err)
|
t.Errorf("params is invalid: %v", err)
|
||||||
@@ -245,19 +265,10 @@ func TestStructToUrlValues(t *testing.T) {
|
|||||||
assert.Equal("1", todoValues.Get("id"))
|
assert.Equal("1", todoValues.Get("id"))
|
||||||
assert.Equal("123", todoValues.Get("userId"))
|
assert.Equal("123", todoValues.Get("userId"))
|
||||||
assert.Equal("", todoValues.Get("name"))
|
assert.Equal("", todoValues.Get("name"))
|
||||||
|
assert.Equal("1.0", todoValues.Get("version"))
|
||||||
item2 := TodoQuery{
|
|
||||||
Id: 2,
|
|
||||||
UserId: 456,
|
|
||||||
}
|
|
||||||
queryValues2, _ := StructToUrlValues(item2)
|
|
||||||
|
|
||||||
assert.Equal("2", queryValues2.Get("id"))
|
|
||||||
assert.Equal("456", queryValues2.Get("userId"))
|
|
||||||
assert.Equal("", queryValues2.Get("name"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleFileRequest(t *testing.T, w http.ResponseWriter, r *http.Request) {
|
func handleFileRequest(t *testing.T, _ http.ResponseWriter, r *http.Request) {
|
||||||
err := r.ParseMultipartForm(1024)
|
err := r.ParseMultipartForm(1024)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -361,3 +372,25 @@ func TestSendRequestWithFilePath(t *testing.T) {
|
|||||||
t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode)
|
t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProxy(t *testing.T) {
|
||||||
|
config := &HttpClientConfig{
|
||||||
|
HandshakeTimeout: 20 * time.Second,
|
||||||
|
ResponseTimeout: 40 * time.Second,
|
||||||
|
// Use the proxy ip to add it here
|
||||||
|
//Proxy: &url.URL{
|
||||||
|
// Scheme: "http",
|
||||||
|
// Host: "46.17.63.166:18888",
|
||||||
|
//},
|
||||||
|
}
|
||||||
|
httpClient := NewHttpClientWithConfig(config)
|
||||||
|
resp, err := httpClient.Get("https://www.ipplus360.com/getLocation")
|
||||||
|
if err != nil {
|
||||||
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,22 +4,31 @@
|
|||||||
// Package pointer contains some util functions to operate go pointer.
|
// Package pointer contains some util functions to operate go pointer.
|
||||||
package pointer
|
package pointer
|
||||||
|
|
||||||
import "reflect"
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
// Of returns a pointer to the value `v`.
|
// Of returns a pointer to the value `v`.
|
||||||
// Play: https://go.dev/play/p/HFd70x4DrMj
|
// Play: https://go.dev/play/p/HFd70x4DrMj
|
||||||
func Of[T any](v T) *T {
|
func Of[T any](v T) *T {
|
||||||
|
if IsNil(v) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unwrap returns the value from the pointer.
|
// Unwrap returns the value from the pointer.
|
||||||
// Play: https://go.dev/play/p/cgeu3g7cjWb
|
//
|
||||||
|
// Play: https://go.dev/play/p/cgeu3g7cjWb
|
||||||
|
// Deprecated: Please use UnwrapOr
|
||||||
func Unwrap[T any](p *T) T {
|
func Unwrap[T any](p *T) T {
|
||||||
return *p
|
return *p
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnwarpOr returns the value from the pointer or fallback if the pointer is nil.
|
// UnwarpOr returns the value from the pointer or fallback if the pointer is nil.
|
||||||
// Play: https://go.dev/play/p/mmNaLC38W8C
|
//
|
||||||
|
// Play: https://go.dev/play/p/mmNaLC38W8C
|
||||||
|
// Deprecated: Please use UnwrapOr
|
||||||
func UnwarpOr[T any](p *T, fallback T) T {
|
func UnwarpOr[T any](p *T, fallback T) T {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return fallback
|
return fallback
|
||||||
@@ -28,7 +37,9 @@ func UnwarpOr[T any](p *T, fallback T) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
|
// UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
|
||||||
// Play: https://go.dev/play/p/ZnGIHf8_o4E
|
//
|
||||||
|
// Play: https://go.dev/play/p/ZnGIHf8_o4E
|
||||||
|
// Deprecated: Please use UnwrapOr
|
||||||
func UnwarpOrDefault[T any](p *T) T {
|
func UnwarpOrDefault[T any](p *T) T {
|
||||||
var v T
|
var v T
|
||||||
|
|
||||||
@@ -38,14 +49,39 @@ func UnwarpOrDefault[T any](p *T) T {
|
|||||||
return *p
|
return *p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnwrapOr returns the value from the pointer or fallback if the pointer is nil.
|
||||||
|
func UnwrapOr[T any](p *T, fallback ...T) T {
|
||||||
|
if !IsNil(p) {
|
||||||
|
return *p
|
||||||
|
}
|
||||||
|
if len(fallback) > 0 {
|
||||||
|
return fallback[0]
|
||||||
|
}
|
||||||
|
var t T
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
// ExtractPointer returns the underlying value by the given interface type
|
// ExtractPointer returns the underlying value by the given interface type
|
||||||
// Play: https://go.dev/play/p/D7HFjeWU2ZP
|
// Play: https://go.dev/play/p/D7HFjeWU2ZP
|
||||||
func ExtractPointer(value any) any {
|
func ExtractPointer(value any) any {
|
||||||
|
if IsNil(value) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
t := reflect.TypeOf(value)
|
t := reflect.TypeOf(value)
|
||||||
v := reflect.ValueOf(value)
|
v := reflect.ValueOf(value)
|
||||||
|
|
||||||
if t.Kind() != reflect.Pointer {
|
if t.Kind() != reflect.Pointer {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
return ExtractPointer(v.Elem().Interface())
|
|
||||||
|
if v.Elem().IsValid() {
|
||||||
|
return ExtractPointer(v.Elem().Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNil returns true if the given interface is nil or the underlying value is nil.
|
||||||
|
func IsNil(i interface{}) bool {
|
||||||
|
return i == nil || (reflect.ValueOf(i).Kind() == reflect.Ptr && reflect.ValueOf(i).IsNil())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,3 +90,61 @@ func ExampleExtractPointer() {
|
|||||||
// Output:
|
// Output:
|
||||||
// 1
|
// 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleIsNil() {
|
||||||
|
a := 1
|
||||||
|
b := &a
|
||||||
|
c := &b
|
||||||
|
d := &c
|
||||||
|
e := &d
|
||||||
|
var f *int
|
||||||
|
|
||||||
|
result1 := IsNil(a)
|
||||||
|
result2 := IsNil(b)
|
||||||
|
result3 := IsNil(c)
|
||||||
|
result4 := IsNil(d)
|
||||||
|
result5 := IsNil(e)
|
||||||
|
result6 := IsNil(f)
|
||||||
|
result7 := IsNil(nil)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
fmt.Println(result5)
|
||||||
|
fmt.Println(result6)
|
||||||
|
fmt.Println(result7)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleUnwrapOr() {
|
||||||
|
a := 123
|
||||||
|
b := "abc"
|
||||||
|
|
||||||
|
var c *int
|
||||||
|
var d *string
|
||||||
|
|
||||||
|
result1 := UnwrapOr(&a, 456)
|
||||||
|
result2 := UnwrapOr(&b, "efg")
|
||||||
|
result3 := UnwrapOr(c, 456)
|
||||||
|
result4 := UnwrapOr(d, "def")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 123
|
||||||
|
// abc
|
||||||
|
// 456
|
||||||
|
// def
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ func TestUnwarpOr(t *testing.T) {
|
|||||||
assert.Equal("def", UnwarpOr(d, "def"))
|
assert.Equal("def", UnwarpOr(d, "def"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnwarpOrDefault(t *testing.T) {
|
func TestUnwrapOrDefault(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
assert := internal.NewAssert(t, "TestUnwarpOrDefault")
|
assert := internal.NewAssert(t, "TestUnwrapOrDefault")
|
||||||
|
|
||||||
a := 123
|
a := 123
|
||||||
b := "abc"
|
b := "abc"
|
||||||
|
|||||||
@@ -8,20 +8,25 @@ import (
|
|||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/mathutil"
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Numeral = "0123456789"
|
MaximumCapacity = math.MaxInt>>1 + 1
|
||||||
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
|
Numeral = "0123456789"
|
||||||
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
|
||||||
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var rn = rand.NewSource(time.Now().UnixNano())
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
}
|
}
|
||||||
@@ -108,18 +113,61 @@ func RandSymbolChar(length int) string {
|
|||||||
return random(SymbolChars, length)
|
return random(SymbolChars, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nearestPowerOfTwo 返回一个大于等于cap的最近的2的整数次幂,参考java8的hashmap的tableSizeFor函数
|
||||||
|
func nearestPowerOfTwo(cap int) int {
|
||||||
|
n := cap - 1
|
||||||
|
n |= n >> 1
|
||||||
|
n |= n >> 2
|
||||||
|
n |= n >> 4
|
||||||
|
n |= n >> 8
|
||||||
|
n |= n >> 16
|
||||||
|
if n < 0 {
|
||||||
|
return 1
|
||||||
|
} else if n >= MaximumCapacity {
|
||||||
|
return MaximumCapacity
|
||||||
|
}
|
||||||
|
return n + 1
|
||||||
|
}
|
||||||
|
|
||||||
// random generate a random string based on given string range.
|
// random generate a random string based on given string range.
|
||||||
func random(s string, length int) string {
|
func random(s string, length int) string {
|
||||||
b := make([]byte, length)
|
// 仿照strings.Builder
|
||||||
|
// 创建一个长度为 length 的字节切片
|
||||||
// fix: https://github.com/duke-git/lancet/issues/75
|
bytes := make([]byte, length)
|
||||||
// r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
strLength := len(s)
|
||||||
|
if strLength <= 0 {
|
||||||
for i := range b {
|
return ""
|
||||||
b[i] = s[rand.Int63()%int64(len(s))]
|
} else if strLength == 1 {
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
bytes[i] = s[0]
|
||||||
|
}
|
||||||
|
return *(*string)(unsafe.Pointer(&bytes))
|
||||||
}
|
}
|
||||||
|
// s的字符需要使用多少个比特位数才能表示完
|
||||||
return string(b)
|
// letterIdBits := int(math.Ceil(math.Log2(strLength))),下面比上面的代码快
|
||||||
|
letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(strLength))))
|
||||||
|
// 最大的字母id掩码
|
||||||
|
var letterIdMask int64 = 1<<letterIdBits - 1
|
||||||
|
// 可用次数的最大值
|
||||||
|
letterIdMax := 63 / letterIdBits
|
||||||
|
// 循环生成随机字符串
|
||||||
|
for i, cache, remain := length-1, rn.Int63(), letterIdMax; i >= 0; {
|
||||||
|
// 检查随机数生成器是否用尽所有随机数
|
||||||
|
if remain == 0 {
|
||||||
|
cache, remain = rn.Int63(), letterIdMax
|
||||||
|
}
|
||||||
|
// 从可用字符的字符串中随机选择一个字符
|
||||||
|
if idx := int(cache & letterIdMask); idx < strLength {
|
||||||
|
bytes[i] = s[idx]
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
// 右移比特位数,为下次选择字符做准备
|
||||||
|
cache >>= letterIdBits
|
||||||
|
remain--
|
||||||
|
}
|
||||||
|
// 仿照strings.Builder用unsafe包返回一个字符串,避免拷贝
|
||||||
|
// 将字节切片转换为字符串并返回
|
||||||
|
return *(*string)(unsafe.Pointer(&bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// UUIdV4 generate a random UUID of version 4 according to RFC 4122.
|
// UUIdV4 generate a random UUID of version 4 according to RFC 4122.
|
||||||
|
|||||||
134
retry/retry.go
134
retry/retry.go
@@ -8,6 +8,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -18,14 +20,14 @@ const (
|
|||||||
// DefaultRetryTimes times of retry
|
// DefaultRetryTimes times of retry
|
||||||
DefaultRetryTimes = 5
|
DefaultRetryTimes = 5
|
||||||
// DefaultRetryDuration time duration of two retries
|
// DefaultRetryDuration time duration of two retries
|
||||||
DefaultRetryDuration = time.Second * 3
|
DefaultRetryLinearInterval = time.Second * 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// RetryConfig is config for retry
|
// RetryConfig is config for retry
|
||||||
type RetryConfig struct {
|
type RetryConfig struct {
|
||||||
context context.Context
|
context context.Context
|
||||||
retryTimes uint
|
retryTimes uint
|
||||||
retryDuration time.Duration
|
backoffStrategy BackoffStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetryFunc is function that retry executes
|
// RetryFunc is function that retry executes
|
||||||
@@ -42,11 +44,59 @@ func RetryTimes(n uint) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetryDuration set duration of retries.
|
// RetryWithCustomBackoff set abitary custom backoff strategy
|
||||||
// Play: https://go.dev/play/p/nk2XRmagfVF
|
// Play: todo
|
||||||
func RetryDuration(d time.Duration) Option {
|
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
|
||||||
|
if backoffStrategy == nil {
|
||||||
|
panic("programming error: backoffStrategy must be not nil")
|
||||||
|
}
|
||||||
|
|
||||||
return func(rc *RetryConfig) {
|
return func(rc *RetryConfig) {
|
||||||
rc.retryDuration = d
|
rc.backoffStrategy = backoffStrategy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryWithLinearBackoff set linear strategy backoff
|
||||||
|
// Play: todo
|
||||||
|
func RetryWithLinearBackoff(interval time.Duration) Option {
|
||||||
|
if interval <= 0 {
|
||||||
|
panic("programming error: retry interval should not be lower or equal to 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(rc *RetryConfig) {
|
||||||
|
rc.backoffStrategy = &linear{
|
||||||
|
interval: interval,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryWithExponentialWithJitterBackoff set exponential strategy backoff
|
||||||
|
// Play: todo
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxJitter < 0 {
|
||||||
|
panic("programming error: retry maxJitter should not be lower to 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if base%2 == 0 {
|
||||||
|
return func(rc *RetryConfig) {
|
||||||
|
rc.backoffStrategy = &shiftExponentialWithJitter{
|
||||||
|
interval: interval,
|
||||||
|
maxJitter: maxJitter,
|
||||||
|
shifter: uint64(math.Log2(float64(base))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(rc *RetryConfig) {
|
||||||
|
rc.backoffStrategy = &exponentialWithJitter{
|
||||||
|
interval: interval,
|
||||||
|
base: time.Duration(base),
|
||||||
|
maxJitter: maxJitter,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,21 +113,26 @@ func Context(ctx context.Context) Option {
|
|||||||
// Play: https://go.dev/play/p/nk2XRmagfVF
|
// Play: https://go.dev/play/p/nk2XRmagfVF
|
||||||
func Retry(retryFunc RetryFunc, opts ...Option) error {
|
func Retry(retryFunc RetryFunc, opts ...Option) error {
|
||||||
config := &RetryConfig{
|
config := &RetryConfig{
|
||||||
retryTimes: DefaultRetryTimes,
|
retryTimes: DefaultRetryTimes,
|
||||||
retryDuration: DefaultRetryDuration,
|
context: context.TODO(),
|
||||||
context: context.TODO(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(config)
|
opt(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.backoffStrategy == nil {
|
||||||
|
config.backoffStrategy = &linear{
|
||||||
|
interval: DefaultRetryLinearInterval,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var i uint
|
var i uint
|
||||||
for i < config.retryTimes {
|
for i < config.retryTimes {
|
||||||
err := retryFunc()
|
err := retryFunc()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
select {
|
select {
|
||||||
case <-time.After(config.retryDuration):
|
case <-time.After(config.backoffStrategy.CalculateInterval()):
|
||||||
case <-config.context.Done():
|
case <-config.context.Done():
|
||||||
return errors.New("retry is cancelled")
|
return errors.New("retry is cancelled")
|
||||||
}
|
}
|
||||||
@@ -93,3 +148,58 @@ func Retry(retryFunc RetryFunc, opts ...Option) error {
|
|||||||
|
|
||||||
return fmt.Errorf("function %s run failed after %d times retry", funcName, i)
|
return fmt.Errorf("function %s run failed after %d times retry", funcName, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackoffStrategy is an interface that defines a method for calculating backoff intervals.
|
||||||
|
type BackoffStrategy interface {
|
||||||
|
// CalculateInterval returns the time.Duration after which the next retry attempt should be made.
|
||||||
|
CalculateInterval() time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// linear is a struct that implements the BackoffStrategy interface using a linear backoff strategy.
|
||||||
|
type linear struct {
|
||||||
|
// interval specifies the fixed duration to wait between retry attempts.
|
||||||
|
interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalculateInterval calculates the next interval returns a constant.
|
||||||
|
func (l *linear) CalculateInterval() time.Duration {
|
||||||
|
return l.interval
|
||||||
|
}
|
||||||
|
|
||||||
|
// exponentialWithJitter is a struct that implements the BackoffStrategy interface using a exponential backoff strategy.
|
||||||
|
type exponentialWithJitter struct {
|
||||||
|
base time.Duration // base is the multiplier for the exponential backoff.
|
||||||
|
interval time.Duration // interval is the current backoff interval, which will be adjusted over time.
|
||||||
|
maxJitter time.Duration // maxJitter is the maximum amount of jitter to apply to the backoff interval.
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalculateInterval calculates the next backoff interval with jitter and updates the interval.
|
||||||
|
func (e *exponentialWithJitter) CalculateInterval() time.Duration {
|
||||||
|
current := e.interval
|
||||||
|
e.interval = e.interval * e.base
|
||||||
|
return current + jitter(e.maxJitter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// shiftExponentialWithJitter is a struct that implements the BackoffStrategy interface using a exponential backoff strategy.
|
||||||
|
type shiftExponentialWithJitter struct {
|
||||||
|
interval time.Duration // interval is the current backoff interval, which will be adjusted over time.
|
||||||
|
maxJitter time.Duration // maxJitter is the maximum amount of jitter to apply to the backoff interval.
|
||||||
|
shifter uint64 // shift by n faster than multiplication
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalculateInterval calculates the next backoff interval with jitter and updates the interval.
|
||||||
|
// Uses shift instead of multiplication
|
||||||
|
func (e *shiftExponentialWithJitter) CalculateInterval() time.Duration {
|
||||||
|
current := e.interval
|
||||||
|
e.interval = e.interval << e.shifter
|
||||||
|
return current + jitter(e.maxJitter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jitter adds a random duration, up to maxJitter,
|
||||||
|
// to the current interval to introduce randomness and avoid synchronized patterns in retry behavior
|
||||||
|
func jitter(maxJitter time.Duration) time.Duration {
|
||||||
|
if maxJitter == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return time.Duration(rand.Int63n(int64(maxJitter)) + 1)
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func ExampleContext() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Retry(increaseNumber,
|
Retry(increaseNumber,
|
||||||
RetryDuration(time.Microsecond*50),
|
RetryWithLinearBackoff(time.Microsecond*50),
|
||||||
Context(ctx),
|
Context(ctx),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ func ExampleContext() {
|
|||||||
// 4
|
// 4
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleRetryDuration() {
|
func ExampleRetryWithLinearBackoff() {
|
||||||
number := 0
|
number := 0
|
||||||
increaseNumber := func() error {
|
increaseNumber := func() error {
|
||||||
number++
|
number++
|
||||||
@@ -40,7 +40,57 @@ func ExampleRetryDuration() {
|
|||||||
return errors.New("error occurs")
|
return errors.New("error occurs")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50))
|
err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(number)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExampleCustomBackoffStrategy struct {
|
||||||
|
interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
|
||||||
|
return c.interval + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleRetryWithCustomBackoff() {
|
||||||
|
number := 0
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
if number == 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(number)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleRetryWithExponentialWithJitterBackoff() {
|
||||||
|
number := 0
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
if number == 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -81,7 +131,7 @@ func ExampleRetry() {
|
|||||||
return errors.New("error occurs")
|
return errors.New("error occurs")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50))
|
err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,86 @@ func TestRetryFailed(t *testing.T) {
|
|||||||
return errors.New("error occurs")
|
return errors.New("error occurs")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50))
|
err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50))
|
||||||
|
|
||||||
assert.IsNotNil(err)
|
assert.IsNotNil(err)
|
||||||
assert.Equal(DefaultRetryTimes, number)
|
assert.Equal(DefaultRetryTimes, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRetryShiftExponentialWithJitterFailed(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestRetryShiftExponentialWithJitterFailed")
|
||||||
|
|
||||||
|
var number int
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
|
||||||
|
|
||||||
|
assert.IsNotNil(err)
|
||||||
|
assert.Equal(DefaultRetryTimes, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRetryExponentialWithJitterFailed(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestRetryExponentialWithJitterFailed")
|
||||||
|
|
||||||
|
var number int
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, time.Microsecond*25))
|
||||||
|
|
||||||
|
assert.IsNotNil(err)
|
||||||
|
assert.Equal(DefaultRetryTimes, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRetryWithExponentialSucceeded(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestRetryWithExponentialSucceeded")
|
||||||
|
|
||||||
|
var number int
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
if number == DefaultRetryTimes {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, time.Microsecond*25))
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(DefaultRetryTimes, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRetryWithExponentialShiftSucceeded(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestRetryWithExponentialShiftSucceeded")
|
||||||
|
|
||||||
|
var number int
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
if number == DefaultRetryTimes {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 4, time.Microsecond*25))
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(DefaultRetryTimes, number)
|
||||||
|
}
|
||||||
|
|
||||||
func TestRetrySucceeded(t *testing.T) {
|
func TestRetrySucceeded(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -40,12 +114,108 @@ func TestRetrySucceeded(t *testing.T) {
|
|||||||
return errors.New("error occurs")
|
return errors.New("error occurs")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50))
|
err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50))
|
||||||
|
|
||||||
assert.IsNil(err)
|
assert.IsNil(err)
|
||||||
assert.Equal(DefaultRetryTimes, number)
|
assert.Equal(DefaultRetryTimes, number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRetryOneShotSucceeded(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestRetryOneShotSucceeded")
|
||||||
|
|
||||||
|
var number int
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50))
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(1, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRetryWitCustomBackoffOneShotSucceeded(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestRetryWitCustomBackoffOneShotSucceeded")
|
||||||
|
|
||||||
|
var number int
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
if number == DefaultRetryTimes {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("error occurs")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithCustomBackoff(&TestCustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(5, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestCustomBackoffStrategy struct {
|
||||||
|
interval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TestCustomBackoffStrategy) CalculateInterval() time.Duration {
|
||||||
|
return c.interval + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRetryWithExponentialWithJitterBackoffShiftOneShotSucceeded(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestRetryWithExponentialWithJitterBackoffShiftOneShotSucceeded")
|
||||||
|
|
||||||
|
var number int
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(1, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRetryWithExponentialWithJitterBackoffOneShotSucceeded(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestRetryWithExponentialWithJitterBackoffOneShotSucceeded")
|
||||||
|
|
||||||
|
var number int
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, time.Microsecond*25))
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(1, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRetryWithExponentialWithJitterBackoffNoJitterOneShotSucceeded(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestRetryWithExponentialWithJitterBackoffNoJitterOneShotSucceeded")
|
||||||
|
|
||||||
|
var number int
|
||||||
|
increaseNumber := func() error {
|
||||||
|
number++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 3, 0))
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(1, number)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSetRetryTimes(t *testing.T) {
|
func TestSetRetryTimes(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -57,7 +227,7 @@ func TestSetRetryTimes(t *testing.T) {
|
|||||||
return errors.New("error occurs")
|
return errors.New("error occurs")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50), RetryTimes(3))
|
err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50), RetryTimes(3))
|
||||||
|
|
||||||
assert.IsNotNil(err)
|
assert.IsNotNil(err)
|
||||||
assert.Equal(3, number)
|
assert.Equal(3, number)
|
||||||
@@ -79,7 +249,7 @@ func TestCancelRetry(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := Retry(increaseNumber,
|
err := Retry(increaseNumber,
|
||||||
RetryDuration(time.Microsecond*50),
|
RetryWithLinearBackoff(time.Microsecond*50),
|
||||||
Context(ctx),
|
Context(ctx),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
237
slice/slice.go
237
slice/slice.go
@@ -50,12 +50,23 @@ func ContainBy[T any](slice []T, predicate func(item T) bool) bool {
|
|||||||
// ContainSubSlice check if the slice contain a given subslice or not.
|
// ContainSubSlice check if the slice contain a given subslice or not.
|
||||||
// Play: https://go.dev/play/p/bcuQ3UT6Sev
|
// Play: https://go.dev/play/p/bcuQ3UT6Sev
|
||||||
func ContainSubSlice[T comparable](slice, subSlice []T) bool {
|
func ContainSubSlice[T comparable](slice, subSlice []T) bool {
|
||||||
for _, v := range subSlice {
|
if len(subSlice) == 0 {
|
||||||
if !Contain(slice, v) {
|
return true
|
||||||
|
}
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
elementMap := make(map[T]struct{}, len(slice))
|
||||||
|
for _, item := range slice {
|
||||||
|
elementMap[item] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range subSlice {
|
||||||
|
if _, ok := elementMap[item]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,26 +92,32 @@ func Chunk[T any](slice []T, size int) [][]T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey.
|
// Compact creates a slice with all falsey values removed. The values false, nil, 0, and "" are falsey.
|
||||||
// Play: https://go.dev/play/p/pO5AnxEr3TK
|
// Play: https://go.dev/play/p/pO5AnxEr3TK
|
||||||
func Compact[T comparable](slice []T) []T {
|
func Compact[T comparable](slice []T) []T {
|
||||||
var zero T
|
var zero T
|
||||||
|
|
||||||
result := []T{}
|
result := make([]T, 0, len(slice))
|
||||||
|
|
||||||
for _, v := range slice {
|
for _, v := range slice {
|
||||||
if v != zero {
|
if v != zero {
|
||||||
result = append(result, v)
|
result = append(result, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result[:len(result):len(result)]
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concat creates a new slice concatenating slice with any additional slices.
|
// Concat creates a new slice concatenating slice with any additional slices.
|
||||||
// Play: https://go.dev/play/p/gPt-q7zr5mk
|
// Play: https://go.dev/play/p/gPt-q7zr5mk
|
||||||
func Concat[T any](slice []T, slices ...[]T) []T {
|
func Concat[T any](slices ...[]T) []T {
|
||||||
result := append([]T{}, slice...)
|
totalLen := 0
|
||||||
|
for _, v := range slices {
|
||||||
|
totalLen += len(v)
|
||||||
|
if totalLen < 0 {
|
||||||
|
panic("len out of range")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result := make([]T, 0, totalLen)
|
||||||
|
|
||||||
for _, v := range slices {
|
for _, v := range slices {
|
||||||
result = append(result, v...)
|
result = append(result, v...)
|
||||||
@@ -109,7 +126,7 @@ func Concat[T any](slice []T, slices ...[]T) []T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Difference creates an slice of whose element in slice but not in comparedSlice.
|
// Difference creates a slice of whose element in slice but not in comparedSlice.
|
||||||
// Play: https://go.dev/play/p/VXvadzLzhDa
|
// Play: https://go.dev/play/p/VXvadzLzhDa
|
||||||
func Difference[T comparable](slice, comparedSlice []T) []T {
|
func Difference[T comparable](slice, comparedSlice []T) []T {
|
||||||
result := []T{}
|
result := []T{}
|
||||||
@@ -498,20 +515,13 @@ func FlatMap[T any, U any](slice []T, iteratee func(index int, item T) []U) []U
|
|||||||
// Reduce creates an slice of values by running each element of slice thru iteratee function.
|
// Reduce creates an slice of values by running each element of slice thru iteratee function.
|
||||||
// Play: https://go.dev/play/p/_RfXJJWIsIm
|
// Play: https://go.dev/play/p/_RfXJJWIsIm
|
||||||
func Reduce[T any](slice []T, iteratee func(index int, item1, item2 T) T, initial T) T {
|
func Reduce[T any](slice []T, iteratee func(index int, item1, item2 T) T, initial T) T {
|
||||||
if len(slice) == 0 {
|
accumulator := initial
|
||||||
return initial
|
|
||||||
|
for i, v := range slice {
|
||||||
|
accumulator = iteratee(i, v, accumulator)
|
||||||
}
|
}
|
||||||
|
|
||||||
result := iteratee(0, initial, slice[0])
|
return accumulator
|
||||||
|
|
||||||
tmp := make([]T, 2)
|
|
||||||
for i := 1; i < len(slice); i++ {
|
|
||||||
tmp[0] = result
|
|
||||||
tmp[1] = slice[i]
|
|
||||||
result = iteratee(i, tmp[0], tmp[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReduceBy produces a value from slice by accumulating the result of each element as passed through the reducer function.
|
// ReduceBy produces a value from slice by accumulating the result of each element as passed through the reducer function.
|
||||||
@@ -625,35 +635,34 @@ func IntSlice(slice any) []int {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAt delete the element of slice from start index to end index - 1.
|
// DeleteAt delete the element of slice at index.
|
||||||
// Play: https://go.dev/play/p/pJ-d6MUWcvK
|
// Play: https://go.dev/play/p/800B1dPBYyd
|
||||||
func DeleteAt[T any](slice []T, start int, end ...int) []T {
|
func DeleteAt[T any](slice []T, index int) []T {
|
||||||
size := len(slice)
|
if index >= len(slice) {
|
||||||
|
index = len(slice) - 1
|
||||||
if start < 0 || start >= size {
|
|
||||||
return slice
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(end) > 0 {
|
result := make([]T, len(slice)-1)
|
||||||
end := end[0]
|
copy(result, slice[:index])
|
||||||
if end <= start {
|
copy(result[index:], slice[index+1:])
|
||||||
return slice
|
|
||||||
}
|
|
||||||
if end > size {
|
|
||||||
end = size
|
|
||||||
}
|
|
||||||
|
|
||||||
slice = append(slice[:start], slice[end:]...)
|
return result
|
||||||
return slice
|
}
|
||||||
|
|
||||||
|
// DeleteRange delete the element of slice from start index to end index(exclude).
|
||||||
|
// Play: https://go.dev/play/p/945HwiNrnle
|
||||||
|
func DeleteRange[T any](slice []T, start, end int) []T {
|
||||||
|
result := make([]T, 0, len(slice)-(end-start))
|
||||||
|
|
||||||
|
for i := 0; i < start; i++ {
|
||||||
|
result = append(result, slice[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
if start == size-1 {
|
for i := end; i < len(slice); i++ {
|
||||||
slice = slice[:start]
|
result = append(result, slice[i])
|
||||||
} else {
|
|
||||||
slice = append(slice[:start], slice[start+1:]...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return slice
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop drop n elements from the start of a slice.
|
// Drop drop n elements from the start of a slice.
|
||||||
@@ -763,21 +772,14 @@ func UpdateAt[T any](slice []T, index int, value T) []T {
|
|||||||
// Play: https://go.dev/play/p/AXw0R3ZTE6a
|
// Play: https://go.dev/play/p/AXw0R3ZTE6a
|
||||||
func Unique[T comparable](slice []T) []T {
|
func Unique[T comparable](slice []T) []T {
|
||||||
result := []T{}
|
result := []T{}
|
||||||
|
exists := map[T]bool{}
|
||||||
for i := 0; i < len(slice); i++ {
|
for _, t := range slice {
|
||||||
v := slice[i]
|
if exists[t] {
|
||||||
skip := true
|
continue
|
||||||
for j := range result {
|
|
||||||
if v == result[j] {
|
|
||||||
skip = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if skip {
|
|
||||||
result = append(result, v)
|
|
||||||
}
|
}
|
||||||
|
exists[t] = true
|
||||||
|
result = append(result, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -794,6 +796,46 @@ func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T {
|
|||||||
return Unique(result)
|
return Unique(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UniqueByField remove duplicate elements in struct slice by struct field.
|
||||||
|
// Play: todo
|
||||||
|
func UniqueByField[T any](slice []T, field string) ([]T, error) {
|
||||||
|
seen := map[any]struct{}{}
|
||||||
|
|
||||||
|
var result []T
|
||||||
|
for _, item := range slice {
|
||||||
|
val, err := getField(item, field)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get field %s failed: %v", field, err)
|
||||||
|
}
|
||||||
|
if _, ok := seen[val]; !ok {
|
||||||
|
seen[val] = struct{}{}
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getField[T any](item T, field string) (interface{}, error) {
|
||||||
|
v := reflect.ValueOf(item)
|
||||||
|
t := reflect.TypeOf(item)
|
||||||
|
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", item)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := v.FieldByName(field)
|
||||||
|
if !f.IsValid() {
|
||||||
|
return nil, fmt.Errorf("field name %s not found", field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.FieldByName(field).Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Union creates a slice of unique elements, in order, from all given slices.
|
// Union creates a slice of unique elements, in order, from all given slices.
|
||||||
// Play: https://go.dev/play/p/hfXV1iRIZOf
|
// Play: https://go.dev/play/p/hfXV1iRIZOf
|
||||||
func Union[T comparable](slices ...[]T) []T {
|
func Union[T comparable](slices ...[]T) []T {
|
||||||
@@ -831,16 +873,11 @@ func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Please use Concat() function instead.
|
||||||
// Merge all given slices into one slice.
|
// Merge all given slices into one slice.
|
||||||
// Play: https://go.dev/play/p/lbjFp784r9N
|
// Play: https://go.dev/play/p/lbjFp784r9N
|
||||||
func Merge[T any](slices ...[]T) []T {
|
func Merge[T any](slices ...[]T) []T {
|
||||||
result := make([]T, 0)
|
return Concat(slices...)
|
||||||
|
|
||||||
for _, v := range slices {
|
|
||||||
result = append(result, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intersection creates a slice of unique elements that included by all slices.
|
// Intersection creates a slice of unique elements that included by all slices.
|
||||||
@@ -1178,6 +1215,22 @@ func AppendIfAbsent[T comparable](slice []T, item T) []T {
|
|||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetToDefaultIf sets elements to their default value if they match the given predicate.
|
||||||
|
// It retains the positions of the elements in the slice.
|
||||||
|
// It returns slice of T and the count of modified slice items
|
||||||
|
// Play: https://go.dev/play/p/9AXGlPRC0-A
|
||||||
|
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int) {
|
||||||
|
var count int
|
||||||
|
for i := 0; i < len(slice); i++ {
|
||||||
|
if predicate(slice[i]) {
|
||||||
|
var zeroValue T
|
||||||
|
slice[i] = zeroValue
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slice, count
|
||||||
|
}
|
||||||
|
|
||||||
// KeyBy converts a slice to a map based on a callback function.
|
// KeyBy converts a slice to a map based on a callback function.
|
||||||
// Play: https://go.dev/play/p/uXod2LWD1Kg
|
// Play: https://go.dev/play/p/uXod2LWD1Kg
|
||||||
func KeyBy[T any, U comparable](slice []T, iteratee func(item T) U) map[U]T {
|
func KeyBy[T any, U comparable](slice []T, iteratee func(item T) U) map[U]T {
|
||||||
@@ -1231,6 +1284,30 @@ func Partition[T any](slice []T, predicates ...func(item T) bool) [][]T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Breaks a list into two parts at the point where the predicate for the first time is true.
|
||||||
|
// Play: https://go.dev/play/p/yLYcBTyeQIz
|
||||||
|
func Break[T any](values []T, predicate func(T) bool) ([]T, []T) {
|
||||||
|
a := make([]T, 0)
|
||||||
|
b := make([]T, 0)
|
||||||
|
if len(values) == 0 {
|
||||||
|
return a, b
|
||||||
|
}
|
||||||
|
matched := false
|
||||||
|
for _, value := range values {
|
||||||
|
|
||||||
|
if !matched && predicate(value) {
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if matched {
|
||||||
|
b = append(b, value)
|
||||||
|
} else {
|
||||||
|
a = append(a, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a, b
|
||||||
|
}
|
||||||
|
|
||||||
// Random get a random item of slice, return idx=-1 when slice is empty
|
// Random get a random item of slice, return idx=-1 when slice is empty
|
||||||
// Play: https://go.dev/play/p/UzpGQptWppw
|
// Play: https://go.dev/play/p/UzpGQptWppw
|
||||||
func Random[T any](slice []T) (val T, idx int) {
|
func Random[T any](slice []T) (val T, idx int) {
|
||||||
@@ -1241,3 +1318,35 @@ func Random[T any](slice []T) (val T, idx int) {
|
|||||||
idx = random.RandInt(0, len(slice))
|
idx = random.RandInt(0, len(slice))
|
||||||
return slice[idx], idx
|
return slice[idx], idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RightPadding adds padding to the right end of a slice.
|
||||||
|
// Play: https://go.dev/play/p/0_2rlLEMBXL
|
||||||
|
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
||||||
|
if paddingLength == 0 {
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
for i := 0; i < paddingLength; i++ {
|
||||||
|
slice = append(slice, paddingValue)
|
||||||
|
}
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftPadding adds padding to the left begin of a slice.
|
||||||
|
// Play: https://go.dev/play/p/jlQVoelLl2k
|
||||||
|
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
||||||
|
if paddingLength == 0 {
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
paddedSlice := make([]T, len(slice)+paddingLength)
|
||||||
|
i := 0
|
||||||
|
for ; i < paddingLength; i++ {
|
||||||
|
paddedSlice[i] = paddingValue
|
||||||
|
}
|
||||||
|
for j := 0; j < len(slice); j++ {
|
||||||
|
paddedSlice[i] = slice[j]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return paddedSlice
|
||||||
|
}
|
||||||
|
|||||||
@@ -594,18 +594,46 @@ func ExampleIntSlice() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExampleDeleteAt() {
|
func ExampleDeleteAt() {
|
||||||
result1 := DeleteAt([]string{"a", "b", "c"}, -1)
|
chars := []string{"a", "b", "c", "d", "e"}
|
||||||
result2 := DeleteAt([]string{"a", "b", "c"}, 0)
|
|
||||||
result3 := DeleteAt([]string{"a", "b", "c"}, 0, 2)
|
result1 := DeleteAt(chars, 0)
|
||||||
|
result2 := DeleteAt(chars, 4)
|
||||||
|
result3 := DeleteAt(chars, 5)
|
||||||
|
result4 := DeleteAt(chars, 6)
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
fmt.Println(result3)
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [a b c]
|
// [b c d e]
|
||||||
// [b c]
|
// [a b c d]
|
||||||
// [c]
|
// [a b c d]
|
||||||
|
// [a b c d]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleDeleteRange() {
|
||||||
|
chars := []string{"a", "b", "c", "d", "e"}
|
||||||
|
|
||||||
|
result1 := DeleteRange(chars, 0, 0)
|
||||||
|
result2 := DeleteRange(chars, 0, 1)
|
||||||
|
result3 := DeleteRange(chars, 0, 3)
|
||||||
|
result4 := DeleteRange(chars, 0, 4)
|
||||||
|
result5 := DeleteRange(chars, 0, 5)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
fmt.Println(result5)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [a b c d e]
|
||||||
|
// [b c d e]
|
||||||
|
// [d e]
|
||||||
|
// [e]
|
||||||
|
// []
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleDrop() {
|
func ExampleDrop() {
|
||||||
@@ -752,6 +780,28 @@ func ExampleUniqueBy() {
|
|||||||
// [1 2 0]
|
// [1 2 0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleUniqueByField() {
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []User{
|
||||||
|
{ID: 1, Name: "a"},
|
||||||
|
{ID: 2, Name: "b"},
|
||||||
|
{ID: 1, Name: "c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := UniqueByField(users, "ID")
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [{1 a} {2 b}]
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleUnion() {
|
func ExampleUnion() {
|
||||||
nums1 := []int{1, 3, 4, 6}
|
nums1 := []int{1, 3, 4, 6}
|
||||||
nums2 := []int{1, 2, 5, 6}
|
nums2 := []int{1, 2, 5, 6}
|
||||||
@@ -1074,3 +1124,42 @@ func ExampleRandom() {
|
|||||||
// Output:
|
// Output:
|
||||||
// okk
|
// okk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleSetToDefaultIf() {
|
||||||
|
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||||
|
modifiedStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||||
|
fmt.Println(modifiedStrs)
|
||||||
|
fmt.Println(count)
|
||||||
|
// Output:
|
||||||
|
// [ b c d ]
|
||||||
|
// 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleBreak() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
even := func(n int) bool { return n%2 == 0 }
|
||||||
|
|
||||||
|
resultEven, resultAfterFirstEven := Break(nums, even)
|
||||||
|
fmt.Println(resultEven)
|
||||||
|
fmt.Println(resultAfterFirstEven)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1]
|
||||||
|
// [2 3 4 5]
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package slice
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -108,6 +109,19 @@ func TestConcat(t *testing.T) {
|
|||||||
assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, []int{5}))
|
assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, []int{5}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkConcat(b *testing.B) {
|
||||||
|
slice1 := []int{1, 2, 3}
|
||||||
|
slice2 := []int{4, 5, 6}
|
||||||
|
slice3 := []int{7, 8, 9}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
result := Concat(slice1, slice2, slice3)
|
||||||
|
if len(result) == 0 {
|
||||||
|
b.Fatal("unexpected empty result")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEqual(t *testing.T) {
|
func TestEqual(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -572,19 +586,27 @@ func TestDeleteAt(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
assert := internal.NewAssert(t, "TestDeleteAt")
|
assert := internal.NewAssert(t, "TestDeleteAt")
|
||||||
|
arr := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
assert.Equal([]string{"a", "b", "c"}, DeleteAt([]string{"a", "b", "c"}, -1))
|
assert.Equal([]int{2, 3, 4, 5}, DeleteAt(arr, 0))
|
||||||
assert.Equal([]string{"a", "b", "c"}, DeleteAt([]string{"a", "b", "c"}, 3))
|
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 4))
|
||||||
assert.Equal([]string{"b", "c"}, DeleteAt([]string{"a", "b", "c"}, 0))
|
|
||||||
assert.Equal([]string{"a", "c"}, DeleteAt([]string{"a", "b", "c"}, 1))
|
|
||||||
assert.Equal([]string{"a", "b"}, DeleteAt([]string{"a", "b", "c"}, 2))
|
|
||||||
|
|
||||||
assert.Equal([]string{"b", "c"}, DeleteAt([]string{"a", "b", "c"}, 0, 1))
|
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 5))
|
||||||
assert.Equal([]string{"c"}, DeleteAt([]string{"a", "b", "c"}, 0, 2))
|
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 6))
|
||||||
assert.Equal([]string{}, DeleteAt([]string{"a", "b", "c"}, 0, 3))
|
|
||||||
assert.Equal([]string{}, DeleteAt([]string{"a", "b", "c"}, 0, 4))
|
assert.Equal([]int{1, 2, 3, 4, 5}, arr)
|
||||||
assert.Equal([]string{"a"}, DeleteAt([]string{"a", "b", "c"}, 1, 3))
|
}
|
||||||
assert.Equal([]string{"a"}, DeleteAt([]string{"a", "b", "c"}, 1, 4))
|
|
||||||
|
func TestDeleteRange(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestDeleteRange")
|
||||||
|
|
||||||
|
arr := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
assert.Equal([]int{1, 2, 3, 4, 5}, DeleteRange(arr, 0, 0))
|
||||||
|
assert.Equal([]int{2, 3, 4, 5}, DeleteRange(arr, 0, 1))
|
||||||
|
assert.Equal([]int{4, 5}, DeleteRange(arr, 0, 3))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDrop(t *testing.T) {
|
func TestDrop(t *testing.T) {
|
||||||
@@ -714,6 +736,33 @@ func TestUniqueBy(t *testing.T) {
|
|||||||
assert.Equal([]int{1, 2, 3, 0}, actual)
|
assert.Equal([]int{1, 2, 3, 0}, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUniqueByField(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestUniqueByField")
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []User{
|
||||||
|
{ID: 1, Name: "a"},
|
||||||
|
{ID: 2, Name: "b"},
|
||||||
|
{ID: 1, Name: "c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueUsers, err := UniqueByField(users, "ID")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal([]User{
|
||||||
|
{ID: 1, Name: "a"},
|
||||||
|
{ID: 2, Name: "b"},
|
||||||
|
}, uniqueUsers)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnion(t *testing.T) {
|
func TestUnion(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -1210,3 +1259,192 @@ func TestRandom(t *testing.T) {
|
|||||||
assert.Greater(len(arr), idx)
|
assert.Greater(len(arr), idx)
|
||||||
assert.Equal(arr[idx], val)
|
assert.Equal(arr[idx], val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetToDefaultIf(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "SetToDefaultIf")
|
||||||
|
|
||||||
|
// Subtest for strings
|
||||||
|
t.Run("strings", func(t *testing.T) {
|
||||||
|
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||||
|
actualStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||||
|
assert.Equal([]string{"", "b", "", "c", "d", ""}, actualStrs)
|
||||||
|
assert.Equal(3, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for integers
|
||||||
|
t.Run("integers", func(t *testing.T) {
|
||||||
|
ints := []int{1, 2, 3, 2, 4, 2}
|
||||||
|
actualInts, count := SetToDefaultIf(ints, func(i int) bool { return i == 2 })
|
||||||
|
assert.Equal([]int{1, 0, 3, 0, 4, 0}, actualInts)
|
||||||
|
assert.Equal(3, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for floating-point numbers
|
||||||
|
t.Run("floats", func(t *testing.T) {
|
||||||
|
floats := []float64{1.1, 2.2, 3.3, 2.2, 4.4, 2.2}
|
||||||
|
actualFloats, count := SetToDefaultIf(floats, func(f float64) bool { return f == 2.2 })
|
||||||
|
assert.Equal([]float64{1.1, 0, 3.3, 0, 4.4, 0}, actualFloats)
|
||||||
|
assert.Equal(3, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for booleans
|
||||||
|
t.Run("booleans", func(t *testing.T) {
|
||||||
|
bools := []bool{true, false, true, true, false}
|
||||||
|
actualBools, count := SetToDefaultIf(bools, func(b bool) bool { return b })
|
||||||
|
assert.Equal([]bool{false, false, false, false, false}, actualBools)
|
||||||
|
assert.Equal(3, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for a custom type
|
||||||
|
type customType struct {
|
||||||
|
field string
|
||||||
|
}
|
||||||
|
t.Run("customType", func(t *testing.T) {
|
||||||
|
customs := []customType{{"a"}, {"b"}, {"a"}, {"c"}}
|
||||||
|
actualCustoms, count := SetToDefaultIf(customs, func(c customType) bool { return c.field == "a" })
|
||||||
|
expected := []customType{{""}, {"b"}, {""}, {"c"}}
|
||||||
|
assert.Equal(expected, actualCustoms)
|
||||||
|
assert.Equal(2, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for slice of integers
|
||||||
|
t.Run("sliceOfInts", func(t *testing.T) {
|
||||||
|
sliceOfInts := [][]int{{1, 2}, {3, 4}, {5, 6}, {1, 2}}
|
||||||
|
actualSlice, count := SetToDefaultIf(sliceOfInts, func(s []int) bool { return reflect.DeepEqual(s, []int{1, 2}) })
|
||||||
|
expected := [][]int{nil, {3, 4}, {5, 6}, nil}
|
||||||
|
assert.Equal(expected, actualSlice)
|
||||||
|
assert.Equal(2, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for maps (simple use case)
|
||||||
|
t.Run("mapOfStringToInts", func(t *testing.T) {
|
||||||
|
maps := []map[string]int{{"a": 1}, {"b": 2}, {"a": 1}, {"c": 3}}
|
||||||
|
actualMaps, count := SetToDefaultIf(maps, func(m map[string]int) bool { _, ok := m["a"]; return ok })
|
||||||
|
expected := []map[string]int{nil, {"b": 2}, nil, {"c": 3}}
|
||||||
|
assert.Equal(expected, actualMaps)
|
||||||
|
assert.Equal(2, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for pointers to integers
|
||||||
|
t.Run("pointersToInts", func(t *testing.T) {
|
||||||
|
one, two, three := 1, 2, 3
|
||||||
|
pointers := []*int{&one, &two, &one, &three}
|
||||||
|
actualPointers, count := SetToDefaultIf(pointers, func(p *int) bool { return p != nil && *p == 1 })
|
||||||
|
expected := []*int{nil, &two, nil, &three}
|
||||||
|
assert.Equal(expected, actualPointers)
|
||||||
|
assert.Equal(2, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for channels
|
||||||
|
t.Run("channels", func(t *testing.T) {
|
||||||
|
ch1, ch2 := make(chan int), make(chan int)
|
||||||
|
channels := []chan int{ch1, ch2, ch1}
|
||||||
|
actualChannels, count := SetToDefaultIf(channels, func(ch chan int) bool { return ch == ch1 })
|
||||||
|
expected := []chan int{nil, ch2, nil}
|
||||||
|
assert.Equal(expected, actualChannels)
|
||||||
|
assert.Equal(2, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for interfaces
|
||||||
|
t.Run("interfaces", func(t *testing.T) {
|
||||||
|
var i1, i2 interface{} = "hello", 42
|
||||||
|
interfaces := []interface{}{i1, i2, i1}
|
||||||
|
actualInterfaces, count := SetToDefaultIf(interfaces, func(i interface{}) bool { _, ok := i.(string); return ok })
|
||||||
|
expected := []interface{}{nil, 42, nil}
|
||||||
|
assert.Equal(expected, actualInterfaces)
|
||||||
|
assert.Equal(2, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for complex structs
|
||||||
|
t.Run("complexStructs", func(t *testing.T) {
|
||||||
|
type ComplexStruct struct {
|
||||||
|
Name string
|
||||||
|
Value int
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
cs1, cs2 := ComplexStruct{Name: "Test", Value: 1, Data: []byte{1, 2, 3}}, ComplexStruct{Name: "Another", Value: 2, Data: []byte{4, 5, 6}}
|
||||||
|
complexStructs := []ComplexStruct{cs1, cs2, cs1}
|
||||||
|
actualComplexStructs, count := SetToDefaultIf(complexStructs, func(cs ComplexStruct) bool { return cs.Name == "Test" })
|
||||||
|
expected := []ComplexStruct{{}, cs2, {}}
|
||||||
|
assert.Equal(expected, actualComplexStructs)
|
||||||
|
assert.Equal(2, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for uints
|
||||||
|
t.Run("uints", func(t *testing.T) {
|
||||||
|
uints := []uint{1, 2, 3, 2, 4, 2}
|
||||||
|
actualUints, count := SetToDefaultIf(uints, func(u uint) bool { return u == 2 })
|
||||||
|
assert.Equal([]uint{1, 0, 3, 0, 4, 0}, actualUints)
|
||||||
|
assert.Equal(3, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for float32
|
||||||
|
t.Run("float32s", func(t *testing.T) {
|
||||||
|
floats := []float32{1.1, 2.2, 3.3, 2.2, 4.4, 2.2}
|
||||||
|
actualFloats, count := SetToDefaultIf(floats, func(f float32) bool { return f == 2.2 })
|
||||||
|
assert.Equal([]float32{1.1, 0, 3.3, 0, 4.4, 0}, actualFloats)
|
||||||
|
assert.Equal(3, count)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Subtest for []byte
|
||||||
|
t.Run("byteSlices", func(t *testing.T) {
|
||||||
|
bytes := [][]byte{{'a', 'b'}, {'c', 'd'}, {'a', 'b'}}
|
||||||
|
actualBytes, count := SetToDefaultIf(bytes, func(b []byte) bool { return string(b) == "ab" })
|
||||||
|
expected := [][]byte{nil, {'c', 'd'}, nil}
|
||||||
|
assert.Equal(expected, actualBytes)
|
||||||
|
assert.Equal(2, count)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBreak(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestBreak")
|
||||||
|
|
||||||
|
// Test with integers
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
even := func(n int) bool { return n%2 == 0 }
|
||||||
|
|
||||||
|
resultEven, resultAfterFirstEven := Break(nums, even)
|
||||||
|
assert.Equal([]int{1}, resultEven)
|
||||||
|
assert.Equal([]int{2, 3, 4, 5}, resultAfterFirstEven)
|
||||||
|
|
||||||
|
// Test with strings
|
||||||
|
strings := []string{"apple", "banana", "cherry", "date", "elderberry"}
|
||||||
|
startsWithA := func(s string) bool { return s[0] == 'a' }
|
||||||
|
|
||||||
|
resultStartsWithA, resultAfterFirstStartsWithA := Break(strings, startsWithA)
|
||||||
|
assert.Equal([]string{}, resultStartsWithA)
|
||||||
|
assert.Equal([]string{"apple", "banana", "cherry", "date", "elderberry"}, resultAfterFirstStartsWithA)
|
||||||
|
|
||||||
|
// Test with empty slice
|
||||||
|
emptySlice := []int{}
|
||||||
|
resultEmpty, _ := Break(emptySlice, even)
|
||||||
|
assert.Equal([]int{}, resultEmpty)
|
||||||
|
|
||||||
|
// Test with all elements satisfying the predicate
|
||||||
|
allEven := []int{2, 4, 6, 8, 10}
|
||||||
|
emptyResult, resultAllEven := Break(allEven, even)
|
||||||
|
assert.Equal([]int{2, 4, 6, 8, 10}, resultAllEven)
|
||||||
|
assert.Equal([]int{}, emptyResult)
|
||||||
|
|
||||||
|
// Test with no elements satisfying the predicate
|
||||||
|
allOdd := []int{1, 3, 5, 7, 9}
|
||||||
|
resultAllOdd, emptyResult := Break(allOdd, even)
|
||||||
|
assert.Equal([]int{1, 3, 5, 7, 9}, resultAllOdd)
|
||||||
|
assert.Equal([]int{}, emptyResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRightPaddingAndLeftPadding(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "RightPaddingAndLeftPadding")
|
||||||
|
|
||||||
|
// Test with integers
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
padded := LeftPadding(RightPadding(nums, 0, 3), 0, 3)
|
||||||
|
assert.Equal([]int{0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0}, padded)
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,19 +47,19 @@ import (
|
|||||||
// Concat(streams ...StreamI[T]) StreamI[T]
|
// Concat(streams ...StreamI[T]) StreamI[T]
|
||||||
// }
|
// }
|
||||||
|
|
||||||
type stream[T any] struct {
|
type Stream[T any] struct {
|
||||||
source []T
|
source []T
|
||||||
}
|
}
|
||||||
|
|
||||||
// Of creates a stream whose elements are the specified values.
|
// Of creates a stream whose elements are the specified values.
|
||||||
// Play: https://go.dev/play/p/jI6_iZZuVFE
|
// Play: https://go.dev/play/p/jI6_iZZuVFE
|
||||||
func Of[T any](elems ...T) stream[T] {
|
func Of[T any](elems ...T) Stream[T] {
|
||||||
return FromSlice(elems)
|
return FromSlice(elems)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate stream where each element is generated by the provided generater function
|
// Generate stream where each element is generated by the provided generater function
|
||||||
// Play: https://go.dev/play/p/rkOWL1yA3j9
|
// Play: https://go.dev/play/p/rkOWL1yA3j9
|
||||||
func Generate[T any](generator func() func() (item T, ok bool)) stream[T] {
|
func Generate[T any](generator func() func() (item T, ok bool)) Stream[T] {
|
||||||
source := make([]T, 0)
|
source := make([]T, 0)
|
||||||
|
|
||||||
var zeroValue T
|
var zeroValue T
|
||||||
@@ -76,13 +76,13 @@ func Generate[T any](generator func() func() (item T, ok bool)) stream[T] {
|
|||||||
|
|
||||||
// FromSlice creates stream from slice.
|
// FromSlice creates stream from slice.
|
||||||
// Play: https://go.dev/play/p/wywTO0XZtI4
|
// Play: https://go.dev/play/p/wywTO0XZtI4
|
||||||
func FromSlice[T any](source []T) stream[T] {
|
func FromSlice[T any](source []T) Stream[T] {
|
||||||
return stream[T]{source: source}
|
return Stream[T]{source: source}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromChannel creates stream from channel.
|
// FromChannel creates stream from channel.
|
||||||
// Play: https://go.dev/play/p/9TZYugGMhXZ
|
// Play: https://go.dev/play/p/9TZYugGMhXZ
|
||||||
func FromChannel[T any](source <-chan T) stream[T] {
|
func FromChannel[T any](source <-chan T) Stream[T] {
|
||||||
s := make([]T, 0)
|
s := make([]T, 0)
|
||||||
|
|
||||||
for v := range source {
|
for v := range source {
|
||||||
@@ -94,7 +94,7 @@ func FromChannel[T any](source <-chan T) stream[T] {
|
|||||||
|
|
||||||
// FromRange creates a number stream from start to end. both start and end are included. [start, end]
|
// FromRange creates a number stream from start to end. both start and end are included. [start, end]
|
||||||
// Play: https://go.dev/play/p/9Ex1-zcg-B-
|
// Play: https://go.dev/play/p/9Ex1-zcg-B-
|
||||||
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) stream[T] {
|
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) Stream[T] {
|
||||||
if end < start {
|
if end < start {
|
||||||
panic("stream.FromRange: param start should be before param end")
|
panic("stream.FromRange: param start should be before param end")
|
||||||
} else if step <= 0 {
|
} else if step <= 0 {
|
||||||
@@ -113,7 +113,7 @@ func FromRange[T constraints.Integer | constraints.Float](start, end, step T) st
|
|||||||
|
|
||||||
// Concat creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream.
|
// Concat creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream.
|
||||||
// Play: https://go.dev/play/p/HM4OlYk_OUC
|
// Play: https://go.dev/play/p/HM4OlYk_OUC
|
||||||
func Concat[T any](a, b stream[T]) stream[T] {
|
func Concat[T any](a, b Stream[T]) Stream[T] {
|
||||||
source := make([]T, 0)
|
source := make([]T, 0)
|
||||||
|
|
||||||
source = append(source, a.source...)
|
source = append(source, a.source...)
|
||||||
@@ -124,7 +124,7 @@ func Concat[T any](a, b stream[T]) stream[T] {
|
|||||||
|
|
||||||
// Distinct returns a stream that removes the duplicated items.
|
// Distinct returns a stream that removes the duplicated items.
|
||||||
// Play: https://go.dev/play/p/eGkOSrm64cB
|
// Play: https://go.dev/play/p/eGkOSrm64cB
|
||||||
func (s stream[T]) Distinct() stream[T] {
|
func (s Stream[T]) Distinct() Stream[T] {
|
||||||
source := make([]T, 0)
|
source := make([]T, 0)
|
||||||
|
|
||||||
distinct := map[string]bool{}
|
distinct := map[string]bool{}
|
||||||
@@ -153,7 +153,7 @@ func hashKey(data any) string {
|
|||||||
|
|
||||||
// Filter returns a stream consisting of the elements of this stream that match the given predicate.
|
// Filter returns a stream consisting of the elements of this stream that match the given predicate.
|
||||||
// Play: https://go.dev/play/p/MFlSANo-buc
|
// Play: https://go.dev/play/p/MFlSANo-buc
|
||||||
func (s stream[T]) Filter(predicate func(item T) bool) stream[T] {
|
func (s Stream[T]) Filter(predicate func(item T) bool) Stream[T] {
|
||||||
source := make([]T, 0)
|
source := make([]T, 0)
|
||||||
|
|
||||||
for _, v := range s.source {
|
for _, v := range s.source {
|
||||||
@@ -167,7 +167,7 @@ func (s stream[T]) Filter(predicate func(item T) bool) stream[T] {
|
|||||||
|
|
||||||
// Map returns a stream consisting of the elements of this stream that apply the given function to elements of stream.
|
// Map returns a stream consisting of the elements of this stream that apply the given function to elements of stream.
|
||||||
// Play: https://go.dev/play/p/OtNQUImdYko
|
// Play: https://go.dev/play/p/OtNQUImdYko
|
||||||
func (s stream[T]) Map(mapper func(item T) T) stream[T] {
|
func (s Stream[T]) Map(mapper func(item T) T) Stream[T] {
|
||||||
source := make([]T, s.Count())
|
source := make([]T, s.Count())
|
||||||
|
|
||||||
for i, v := range s.source {
|
for i, v := range s.source {
|
||||||
@@ -179,7 +179,7 @@ func (s stream[T]) Map(mapper func(item T) T) stream[T] {
|
|||||||
|
|
||||||
// Peek returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
|
// Peek returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
|
||||||
// Play: https://go.dev/play/p/u1VNzHs6cb2
|
// Play: https://go.dev/play/p/u1VNzHs6cb2
|
||||||
func (s stream[T]) Peek(consumer func(item T)) stream[T] {
|
func (s Stream[T]) Peek(consumer func(item T)) Stream[T] {
|
||||||
for _, v := range s.source {
|
for _, v := range s.source {
|
||||||
consumer(v)
|
consumer(v)
|
||||||
}
|
}
|
||||||
@@ -190,7 +190,7 @@ func (s stream[T]) Peek(consumer func(item T)) stream[T] {
|
|||||||
// Skip returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream.
|
// Skip returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream.
|
||||||
// If this stream contains fewer than n elements then an empty stream will be returned.
|
// If this stream contains fewer than n elements then an empty stream will be returned.
|
||||||
// Play: https://go.dev/play/p/fNdHbqjahum
|
// Play: https://go.dev/play/p/fNdHbqjahum
|
||||||
func (s stream[T]) Skip(n int) stream[T] {
|
func (s Stream[T]) Skip(n int) Stream[T] {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@@ -211,7 +211,7 @@ func (s stream[T]) Skip(n int) stream[T] {
|
|||||||
|
|
||||||
// Limit returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length.
|
// Limit returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length.
|
||||||
// Play: https://go.dev/play/p/qsO4aniDcGf
|
// Play: https://go.dev/play/p/qsO4aniDcGf
|
||||||
func (s stream[T]) Limit(maxSize int) stream[T] {
|
func (s Stream[T]) Limit(maxSize int) Stream[T] {
|
||||||
if s.source == nil {
|
if s.source == nil {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@@ -231,7 +231,7 @@ func (s stream[T]) Limit(maxSize int) stream[T] {
|
|||||||
|
|
||||||
// AllMatch returns whether all elements of this stream match the provided predicate.
|
// AllMatch returns whether all elements of this stream match the provided predicate.
|
||||||
// Play: https://go.dev/play/p/V5TBpVRs-Cx
|
// Play: https://go.dev/play/p/V5TBpVRs-Cx
|
||||||
func (s stream[T]) AllMatch(predicate func(item T) bool) bool {
|
func (s Stream[T]) AllMatch(predicate func(item T) bool) bool {
|
||||||
for _, v := range s.source {
|
for _, v := range s.source {
|
||||||
if !predicate(v) {
|
if !predicate(v) {
|
||||||
return false
|
return false
|
||||||
@@ -243,7 +243,7 @@ func (s stream[T]) AllMatch(predicate func(item T) bool) bool {
|
|||||||
|
|
||||||
// AnyMatch returns whether any elements of this stream match the provided predicate.
|
// AnyMatch returns whether any elements of this stream match the provided predicate.
|
||||||
// Play: https://go.dev/play/p/PTCnWn4OxSn
|
// Play: https://go.dev/play/p/PTCnWn4OxSn
|
||||||
func (s stream[T]) AnyMatch(predicate func(item T) bool) bool {
|
func (s Stream[T]) AnyMatch(predicate func(item T) bool) bool {
|
||||||
for _, v := range s.source {
|
for _, v := range s.source {
|
||||||
if predicate(v) {
|
if predicate(v) {
|
||||||
return true
|
return true
|
||||||
@@ -255,13 +255,13 @@ func (s stream[T]) AnyMatch(predicate func(item T) bool) bool {
|
|||||||
|
|
||||||
// NoneMatch returns whether no elements of this stream match the provided predicate.
|
// NoneMatch returns whether no elements of this stream match the provided predicate.
|
||||||
// Play: https://go.dev/play/p/iWS64pL1oo3
|
// Play: https://go.dev/play/p/iWS64pL1oo3
|
||||||
func (s stream[T]) NoneMatch(predicate func(item T) bool) bool {
|
func (s Stream[T]) NoneMatch(predicate func(item T) bool) bool {
|
||||||
return !s.AnyMatch(predicate)
|
return !s.AnyMatch(predicate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForEach performs an action for each element of this stream.
|
// ForEach performs an action for each element of this stream.
|
||||||
// Play: https://go.dev/play/p/Dsm0fPqcidk
|
// Play: https://go.dev/play/p/Dsm0fPqcidk
|
||||||
func (s stream[T]) ForEach(action func(item T)) {
|
func (s Stream[T]) ForEach(action func(item T)) {
|
||||||
for _, v := range s.source {
|
for _, v := range s.source {
|
||||||
action(v)
|
action(v)
|
||||||
}
|
}
|
||||||
@@ -269,7 +269,7 @@ func (s stream[T]) ForEach(action func(item T)) {
|
|||||||
|
|
||||||
// Reduce performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any.
|
// Reduce performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any.
|
||||||
// Play: https://go.dev/play/p/6uzZjq_DJLU
|
// Play: https://go.dev/play/p/6uzZjq_DJLU
|
||||||
func (s stream[T]) Reduce(initial T, accumulator func(a, b T) T) T {
|
func (s Stream[T]) Reduce(initial T, accumulator func(a, b T) T) T {
|
||||||
for _, v := range s.source {
|
for _, v := range s.source {
|
||||||
initial = accumulator(initial, v)
|
initial = accumulator(initial, v)
|
||||||
}
|
}
|
||||||
@@ -279,13 +279,13 @@ func (s stream[T]) Reduce(initial T, accumulator func(a, b T) T) T {
|
|||||||
|
|
||||||
// Count returns the count of elements in the stream.
|
// Count returns the count of elements in the stream.
|
||||||
// Play: https://go.dev/play/p/r3koY6y_Xo-
|
// Play: https://go.dev/play/p/r3koY6y_Xo-
|
||||||
func (s stream[T]) Count() int {
|
func (s Stream[T]) Count() int {
|
||||||
return len(s.source)
|
return len(s.source)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindFirst returns the first element of this stream and true, or zero value and false if the stream is empty.
|
// FindFirst returns the first element of this stream and true, or zero value and false if the stream is empty.
|
||||||
// Play: https://go.dev/play/p/9xEf0-6C1e3
|
// Play: https://go.dev/play/p/9xEf0-6C1e3
|
||||||
func (s stream[T]) FindFirst() (T, bool) {
|
func (s Stream[T]) FindFirst() (T, bool) {
|
||||||
var result T
|
var result T
|
||||||
|
|
||||||
if s.source == nil || len(s.source) == 0 {
|
if s.source == nil || len(s.source) == 0 {
|
||||||
@@ -297,7 +297,7 @@ func (s stream[T]) FindFirst() (T, bool) {
|
|||||||
|
|
||||||
// FindLast returns the last element of this stream and true, or zero value and false if the stream is empty.
|
// FindLast returns the last element of this stream and true, or zero value and false if the stream is empty.
|
||||||
// Play: https://go.dev/play/p/WZD2rDAW-2h
|
// Play: https://go.dev/play/p/WZD2rDAW-2h
|
||||||
func (s stream[T]) FindLast() (T, bool) {
|
func (s Stream[T]) FindLast() (T, bool) {
|
||||||
var result T
|
var result T
|
||||||
|
|
||||||
if s.source == nil || len(s.source) == 0 {
|
if s.source == nil || len(s.source) == 0 {
|
||||||
@@ -309,7 +309,7 @@ func (s stream[T]) FindLast() (T, bool) {
|
|||||||
|
|
||||||
// Reverse returns a stream whose elements are reverse order of given stream.
|
// Reverse returns a stream whose elements are reverse order of given stream.
|
||||||
// Play: https://go.dev/play/p/A8_zkJnLHm4
|
// Play: https://go.dev/play/p/A8_zkJnLHm4
|
||||||
func (s stream[T]) Reverse() stream[T] {
|
func (s Stream[T]) Reverse() Stream[T] {
|
||||||
l := len(s.source)
|
l := len(s.source)
|
||||||
source := make([]T, l)
|
source := make([]T, l)
|
||||||
|
|
||||||
@@ -321,7 +321,7 @@ func (s stream[T]) Reverse() stream[T] {
|
|||||||
|
|
||||||
// Range returns a stream whose elements are in the range from start(included) to end(excluded) original stream.
|
// Range returns a stream whose elements are in the range from start(included) to end(excluded) original stream.
|
||||||
// Play: https://go.dev/play/p/indZY5V2f4j
|
// Play: https://go.dev/play/p/indZY5V2f4j
|
||||||
func (s stream[T]) Range(start, end int) stream[T] {
|
func (s Stream[T]) Range(start, end int) Stream[T] {
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
}
|
}
|
||||||
@@ -347,7 +347,7 @@ func (s stream[T]) Range(start, end int) stream[T] {
|
|||||||
|
|
||||||
// Sorted returns a stream consisting of the elements of this stream, sorted according to the provided less function.
|
// Sorted returns a stream consisting of the elements of this stream, sorted according to the provided less function.
|
||||||
// Play: https://go.dev/play/p/XXtng5uonFj
|
// Play: https://go.dev/play/p/XXtng5uonFj
|
||||||
func (s stream[T]) Sorted(less func(a, b T) bool) stream[T] {
|
func (s Stream[T]) Sorted(less func(a, b T) bool) Stream[T] {
|
||||||
source := []T{}
|
source := []T{}
|
||||||
source = append(source, s.source...)
|
source = append(source, s.source...)
|
||||||
|
|
||||||
@@ -359,7 +359,7 @@ func (s stream[T]) Sorted(less func(a, b T) bool) stream[T] {
|
|||||||
// Max returns the maximum element of this stream according to the provided less function.
|
// Max returns the maximum element of this stream according to the provided less function.
|
||||||
// less: a > b
|
// less: a > b
|
||||||
// Play: https://go.dev/play/p/fm-1KOPtGzn
|
// Play: https://go.dev/play/p/fm-1KOPtGzn
|
||||||
func (s stream[T]) Max(less func(a, b T) bool) (T, bool) {
|
func (s Stream[T]) Max(less func(a, b T) bool) (T, bool) {
|
||||||
var max T
|
var max T
|
||||||
|
|
||||||
if len(s.source) == 0 {
|
if len(s.source) == 0 {
|
||||||
@@ -377,7 +377,7 @@ func (s stream[T]) Max(less func(a, b T) bool) (T, bool) {
|
|||||||
// Min returns the minimum element of this stream according to the provided less function.
|
// Min returns the minimum element of this stream according to the provided less function.
|
||||||
// less: a < b
|
// less: a < b
|
||||||
// Play: https://go.dev/play/p/vZfIDgGNRe_0
|
// Play: https://go.dev/play/p/vZfIDgGNRe_0
|
||||||
func (s stream[T]) Min(less func(a, b T) bool) (T, bool) {
|
func (s Stream[T]) Min(less func(a, b T) bool) (T, bool) {
|
||||||
var min T
|
var min T
|
||||||
|
|
||||||
if len(s.source) == 0 {
|
if len(s.source) == 0 {
|
||||||
@@ -395,6 +395,6 @@ func (s stream[T]) Min(less func(a, b T) bool) (T, bool) {
|
|||||||
|
|
||||||
// ToSlice return the elements in the stream.
|
// ToSlice return the elements in the stream.
|
||||||
// Play: https://go.dev/play/p/jI6_iZZuVFE
|
// Play: https://go.dev/play/p/jI6_iZZuVFE
|
||||||
func (s stream[T]) ToSlice() []T {
|
func (s Stream[T]) ToSlice() []T {
|
||||||
return s.source
|
return s.source
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,16 @@ func (f *Field) IsZero() bool {
|
|||||||
return reflect.DeepEqual(z, v)
|
return reflect.DeepEqual(z, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNil returns true if the given field is nil value.
|
||||||
|
func (f *Field) IsNil() bool {
|
||||||
|
v := f.Value()
|
||||||
|
if v == nil || (reflect.ValueOf(v)).Kind() == reflect.Ptr && reflect.ValueOf(v).IsNil() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Name returns the name of the given field
|
// Name returns the name of the given field
|
||||||
func (f *Field) Name() string {
|
func (f *Field) Name() string {
|
||||||
return f.field.Name
|
return f.field.Name
|
||||||
@@ -111,7 +121,9 @@ func (f *Field) mapValue(value any) any {
|
|||||||
ret = v.Interface()
|
ret = v.Interface()
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
ret = v.Interface()
|
if v.Kind().String() != "invalid" {
|
||||||
|
ret = v.Interface()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
package structs
|
package structs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/pointer"
|
"github.com/duke-git/lancet/v2/pointer"
|
||||||
@@ -61,7 +62,7 @@ func New(value any, tagName ...string) *Struct {
|
|||||||
// ToMap convert the exported fields of a struct to map.
|
// ToMap convert the exported fields of a struct to map.
|
||||||
func (s *Struct) ToMap() (map[string]any, error) {
|
func (s *Struct) ToMap() (map[string]any, error) {
|
||||||
if !s.IsStruct() {
|
if !s.IsStruct() {
|
||||||
return nil, errInvalidStruct(s)
|
return nil, fmt.Errorf("invalid struct %v", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make(map[string]any)
|
result := make(map[string]any)
|
||||||
@@ -70,9 +71,15 @@ func (s *Struct) ToMap() (map[string]any, error) {
|
|||||||
if !f.IsExported() || f.tag.IsEmpty() || f.tag.Name == "-" {
|
if !f.IsExported() || f.tag.IsEmpty() || f.tag.Name == "-" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.IsZero() && f.tag.HasOption("omitempty") {
|
if f.IsZero() && f.tag.HasOption("omitempty") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if f.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
result[f.tag.Name] = f.mapValue(f.Value())
|
result[f.tag.Name] = f.mapValue(f.Value())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package structs
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
func errInvalidStruct(v any) error {
|
|
||||||
return fmt.Errorf("invalid struct %v", v)
|
|
||||||
}
|
|
||||||
@@ -65,6 +65,45 @@ func TestStruct_ToMap(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_ToMap2(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestStruct_ToMap")
|
||||||
|
|
||||||
|
type M struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type S struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
M *M `json:"m"`
|
||||||
|
}
|
||||||
|
|
||||||
|
s1 := &S{
|
||||||
|
ID: 1,
|
||||||
|
}
|
||||||
|
var expect1 = map[string]any{"id": 1}
|
||||||
|
map1, err := ToMap(s1)
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(expect1, map1)
|
||||||
|
|
||||||
|
s2 := &S{
|
||||||
|
ID: 1,
|
||||||
|
M: &M{
|
||||||
|
Name: "test",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var expect2 = map[string]any{
|
||||||
|
"id": 1,
|
||||||
|
"m": map[string]any{
|
||||||
|
"name": "test",
|
||||||
|
}}
|
||||||
|
map2, err := ToMap(s2)
|
||||||
|
|
||||||
|
assert.IsNil(err)
|
||||||
|
assert.Equal(expect2, map2)
|
||||||
|
}
|
||||||
|
|
||||||
func TestStruct_Fields(t *testing.T) {
|
func TestStruct_Fields(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
package strutil
|
package strutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"errors"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
@@ -122,40 +122,48 @@ func UpperSnakeCase(s string) string {
|
|||||||
// Before returns the substring of the source string up to the first occurrence of the specified string.
|
// Before returns the substring of the source string up to the first occurrence of the specified string.
|
||||||
// Play: https://go.dev/play/p/JAWTZDS4F5w
|
// Play: https://go.dev/play/p/JAWTZDS4F5w
|
||||||
func Before(s, char string) string {
|
func Before(s, char string) string {
|
||||||
if s == "" || char == "" {
|
i := strings.Index(s, char)
|
||||||
|
|
||||||
|
if s == "" || char == "" || i == -1 {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
i := strings.Index(s, char)
|
|
||||||
return s[0:i]
|
return s[0:i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeforeLast returns the substring of the source string up to the last occurrence of the specified string.
|
// BeforeLast returns the substring of the source string up to the last occurrence of the specified string.
|
||||||
// Play: https://go.dev/play/p/pJfXXAoG_Te
|
// Play: https://go.dev/play/p/pJfXXAoG_Te
|
||||||
func BeforeLast(s, char string) string {
|
func BeforeLast(s, char string) string {
|
||||||
if s == "" || char == "" {
|
i := strings.LastIndex(s, char)
|
||||||
|
|
||||||
|
if s == "" || char == "" || i == -1 {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
i := strings.LastIndex(s, char)
|
|
||||||
return s[0:i]
|
return s[0:i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// After returns the substring after the first occurrence of a specified string in the source string.
|
// After returns the substring after the first occurrence of a specified string in the source string.
|
||||||
// Play: https://go.dev/play/p/RbCOQqCDA7m
|
// Play: https://go.dev/play/p/RbCOQqCDA7m
|
||||||
func After(s, char string) string {
|
func After(s, char string) string {
|
||||||
if s == "" || char == "" {
|
i := strings.Index(s, char)
|
||||||
|
|
||||||
|
if s == "" || char == "" || i == -1 {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
i := strings.Index(s, char)
|
|
||||||
return s[i+len(char):]
|
return s[i+len(char):]
|
||||||
}
|
}
|
||||||
|
|
||||||
// AfterLast returns the substring after the last occurrence of a specified string in the source string.
|
// AfterLast returns the substring after the last occurrence of a specified string in the source string.
|
||||||
// Play: https://go.dev/play/p/1TegARrb8Yn
|
// Play: https://go.dev/play/p/1TegARrb8Yn
|
||||||
func AfterLast(s, char string) string {
|
func AfterLast(s, char string) string {
|
||||||
if s == "" || char == "" {
|
i := strings.LastIndex(s, char)
|
||||||
|
|
||||||
|
if s == "" || char == "" || i == -1 {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
i := strings.LastIndex(s, char)
|
|
||||||
return s[i+len(char):]
|
return s[i+len(char):]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,10 +388,7 @@ func RemoveNonPrintable(str string) string {
|
|||||||
// StringToBytes converts a string to byte slice without a memory allocation.
|
// StringToBytes converts a string to byte slice without a memory allocation.
|
||||||
// Play: https://go.dev/play/p/7OyFBrf9AxA
|
// Play: https://go.dev/play/p/7OyFBrf9AxA
|
||||||
func StringToBytes(str string) (b []byte) {
|
func StringToBytes(str string) (b []byte) {
|
||||||
sh := *(*reflect.StringHeader)(unsafe.Pointer(&str))
|
return *(*[]byte)(unsafe.Pointer(&str))
|
||||||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
|
||||||
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
|
|
||||||
return b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BytesToString converts a byte slice to string without a memory allocation.
|
// BytesToString converts a byte slice to string without a memory allocation.
|
||||||
@@ -578,3 +583,58 @@ func RemoveWhiteSpace(str string, repalceAll bool) string {
|
|||||||
|
|
||||||
return strings.TrimSpace(str)
|
return strings.TrimSpace(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubInBetween return substring between the start and end position(excluded) of source string.
|
||||||
|
// Play: https://go.dev/play/p/EDbaRvjeNsv
|
||||||
|
func SubInBetween(str string, start string, end string) string {
|
||||||
|
if _, after, ok := strings.Cut(str, start); ok {
|
||||||
|
if before, _, ok := strings.Cut(after, end); ok {
|
||||||
|
return before
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// HammingDistance calculates the Hamming distance between two strings.
|
||||||
|
// The Hamming distance is the number of positions at which the corresponding symbols are different.
|
||||||
|
// This func returns an error if the input strings are of unequal lengths.
|
||||||
|
// Play: https://go.dev/play/p/glNdQEA9HUi
|
||||||
|
func HammingDistance(a, b string) (int, error) {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return -1, errors.New("a length and b length are unequal")
|
||||||
|
}
|
||||||
|
|
||||||
|
ar := []rune(a)
|
||||||
|
br := []rune(b)
|
||||||
|
|
||||||
|
var distance int
|
||||||
|
for i, codepoint := range ar {
|
||||||
|
if codepoint != br[i] {
|
||||||
|
distance++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return distance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func Concat(length int, str ...string) string {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
sb := strings.Builder{}
|
||||||
|
if length <= 0 {
|
||||||
|
sb.Grow(len(str[0]) * len(str))
|
||||||
|
} else {
|
||||||
|
sb.Grow(length)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range str {
|
||||||
|
sb.WriteString(s)
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|||||||
@@ -653,3 +653,44 @@ func ExampleRemoveWhiteSpace() {
|
|||||||
// helloworld
|
// helloworld
|
||||||
// hello world
|
// hello world
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleSubInBetween() {
|
||||||
|
str := "abcde"
|
||||||
|
|
||||||
|
result1 := SubInBetween(str, "", "de")
|
||||||
|
result2 := SubInBetween(str, "a", "d")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// abc
|
||||||
|
// bc
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleHammingDistance() {
|
||||||
|
|
||||||
|
result, _ := HammingDistance("abc", "def")
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
result, _ = HammingDistance("name", "namf")
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 3
|
||||||
|
// 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleConcat() {
|
||||||
|
result1 := Concat(12, "Hello", " ", "World", "!")
|
||||||
|
result2 := Concat(11, "Go", " ", "Language")
|
||||||
|
result3 := 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 day,keeps the doctor away
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user