mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
33
Commits
e95d7c82cd
...
v2.3.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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 `rc` 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.
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
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
|
||||||
@@ -660,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)]
|
||||||
@@ -729,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>
|
||||||
|
|
||||||
@@ -791,20 +800,28 @@ import "github.com/duke-git/lancet/v2/function"
|
|||||||
[[play](https://go.dev/play/p/hbON-Xeyn5N)]
|
[[play](https://go.dev/play/p/hbON-Xeyn5N)]
|
||||||
- **<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)]
|
||||||
- **<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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
||||||
@@ -884,6 +901,10 @@ 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>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)]
|
||||||
|
- **<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)]
|
||||||
- **<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)]
|
||||||
@@ -953,6 +974,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)]
|
||||||
@@ -992,6 +1025,10 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
|||||||
- **<big>Abs</big>** : returns the absolute value of param number.
|
- **<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>
|
||||||
|
|
||||||
@@ -1161,6 +1198,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>
|
||||||
|
|
||||||
@@ -1388,6 +1435,14 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
[[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.
|
- **<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)]
|
[[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)]
|
||||||
|
- **<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)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<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>
|
||||||
@@ -1635,8 +1690,11 @@ import "github.com/duke-git/lancet/v2/strutil"
|
|||||||
[[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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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>
|
||||||
|
|
||||||
@@ -1976,7 +2034,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)
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
Thank you to all the people who contributed to lancet!
|
Thank you to all the people who contributed to lancet!
|
||||||
|
|||||||
+65
-4
@@ -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
|
||||||
@@ -658,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)]
|
||||||
@@ -727,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>
|
||||||
|
|
||||||
@@ -792,22 +804,31 @@ import "github.com/duke-git/lancet/v2/function"
|
|||||||
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
|
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
|
||||||
- **<big>AcceptIf</big>** : AcceptIf函数会返回另一个函数,该函数的签名与apply函数相同,但同时还会包含一个布尔值来表示成功或失败。
|
- **<big>AcceptIf</big>** : AcceptIf函数会返回另一个函数,该函数的签名与apply函数相同,但同时还会包含一个布尔值来表示成功或失败。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#AcceptIf)]
|
[[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操作。
|
- **<big>And</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑and操作。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#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操作。
|
- **<big>Or</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑or操作。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#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>** : 返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。
|
- **<big>Negate</big>** : 返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Negate)]
|
[[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的操作。
|
- **<big>Nor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非或nor的操作。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#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的操作。
|
- **<big>Nand</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非与nand的操作。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#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的操作。
|
- **<big>Xnor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑异或xnor的操作。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#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
|
||||||
@@ -882,6 +903,10 @@ import "github.com/duke-git/lancet/v2/maputil"
|
|||||||
- **<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)]
|
||||||
[[play](https://go.dev/play/p/isZZHOsDhFc)]
|
[[play](https://go.dev/play/p/isZZHOsDhFc)]
|
||||||
|
- **<big>ToSortedSlicesDefault</big>** : 将map的key和value转化成两个根据key的值从小到大排序的切片,value切片中元素的位置与key对应。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesDefault)]
|
||||||
|
- **<big>ToSortedSlicesWithComparator</big>** : 将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片,value切片中元素的位置与key对应。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesWithComparator)]
|
||||||
- **<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)]
|
||||||
@@ -951,6 +976,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)]
|
||||||
@@ -990,6 +1027,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>
|
||||||
|
|
||||||
@@ -1157,6 +1198,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>
|
||||||
|
|
||||||
@@ -1383,7 +1434,13 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
[[play](https://go.dev/play/p/UzpGQptWppw)]
|
[[play](https://go.dev/play/p/UzpGQptWppw)]
|
||||||
- **<big>SetToDefaultIf</big>** : 根据给定给定的predicate判定函数来修改切片中的元素。
|
- **<big>SetToDefaultIf</big>** : 根据给定给定的predicate判定函数来修改切片中的元素。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#SetToDefaultIf)]
|
[[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)]
|
||||||
|
- **<big>LeftPadding</big>** : 在切片的左部添加元素。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#LeftPadding)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="stream"> 19. stream 流,该包仅验证简单的 stream 实现,功能有限。 <a href="#index">回到目录</a></h3>
|
<h3 id="stream"> 19. stream 流,该包仅验证简单的 stream 实现,功能有限。 <a href="#index">回到目录</a></h3>
|
||||||
@@ -1473,6 +1530,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
|
||||||
@@ -1634,8 +1692,11 @@ import "github.com/duke-git/lancet/v2/strutil"
|
|||||||
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
|
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
|
||||||
- **<big>SubInBetween</big>** : 获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。
|
- **<big>SubInBetween</big>** : 获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SubInBetween)]
|
[[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>** : 计算两个字符串之间的汉明距离。
|
- **<big>HammingDistance</big>** : 计算两个字符串之间的汉明距离。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HammingDistance)]
|
[[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>
|
||||||
|
|
||||||
|
|||||||
@@ -397,6 +397,7 @@ func GbkToUtf8(bs []byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToStdBase64 convert data to standard base64 encoding.
|
// ToStdBase64 convert data to standard base64 encoding.
|
||||||
|
// Play: https://go.dev/play/p/_fLJqJD3NMo
|
||||||
func ToStdBase64(value any) string {
|
func ToStdBase64(value any) string {
|
||||||
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||||
return ""
|
return ""
|
||||||
@@ -418,6 +419,7 @@ func ToStdBase64(value any) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToUrlBase64 convert data to URL base64 encoding.
|
// ToUrlBase64 convert data to URL base64 encoding.
|
||||||
|
// Play: https://go.dev/play/p/C_d0GlvEeUR
|
||||||
func ToUrlBase64(value any) string {
|
func ToUrlBase64(value any) string {
|
||||||
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||||
return ""
|
return ""
|
||||||
@@ -439,6 +441,7 @@ func ToUrlBase64(value any) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToRawStdBase64 convert data to raw standard base64 encoding.
|
// ToRawStdBase64 convert data to raw standard base64 encoding.
|
||||||
|
// Play: https://go.dev/play/p/wSAr3sfkDcv
|
||||||
func ToRawStdBase64(value any) string {
|
func ToRawStdBase64(value any) string {
|
||||||
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||||
return ""
|
return ""
|
||||||
@@ -460,6 +463,7 @@ func ToRawStdBase64(value any) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ToRawUrlBase64 convert data to raw URL base64 encoding.
|
// ToRawUrlBase64 convert data to raw URL base64 encoding.
|
||||||
|
// Play: https://go.dev/play/p/HwdDPFcza1O
|
||||||
func ToRawUrlBase64(value any) string {
|
func ToRawUrlBase64(value any) string {
|
||||||
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -466,12 +466,6 @@ func TestGbkToUtf8(t *testing.T) {
|
|||||||
assert.Equal("hello", string(utf8Data))
|
assert.Equal("hello", string(utf8Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeBase64(data []byte) ([]byte, error) {
|
|
||||||
buf := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
|
|
||||||
n, err := base64.StdEncoding.Decode(buf, data)
|
|
||||||
return buf[:n], err
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToStdBase64(t *testing.T) {
|
func TestToStdBase64(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
assert := internal.NewAssert(t, "TestToStdBase64")
|
assert := internal.NewAssert(t, "TestToStdBase64")
|
||||||
|
|||||||
@@ -187,10 +187,11 @@ 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
|
||||||
|
|||||||
@@ -243,25 +243,34 @@ 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 := New[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 = New(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) {
|
func TestSet_ToSlice(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|||||||
+138
-138
@@ -891,7 +891,7 @@ func main() {
|
|||||||
func ToStdBase64(value any) string
|
func ToStdBase64(value any) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/_fLJqJD3NMo)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -903,52 +903,52 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
afterEncode := convertor.ToStdBase64(nil)
|
afterEncode := convertor.ToStdBase64(nil)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
afterEncode = convertor.ToStdBase64("")
|
afterEncode = convertor.ToStdBase64("")
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
stringVal := "hello"
|
stringVal := "hello"
|
||||||
afterEncode = convertor.ToStdBase64(stringVal)
|
afterEncode = convertor.ToStdBase64(stringVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
byteSliceVal := []byte("hello")
|
byteSliceVal := []byte("hello")
|
||||||
afterEncode = convertor.ToStdBase64(byteSliceVal)
|
afterEncode = convertor.ToStdBase64(byteSliceVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
intVal := 123
|
intVal := 123
|
||||||
afterEncode = convertor.ToStdBase64(intVal)
|
afterEncode = convertor.ToStdBase64(intVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
A string
|
A string
|
||||||
B int
|
B int
|
||||||
}{"hello", 3}}
|
}{"hello", 3}}
|
||||||
afterEncode = convertor.ToStdBase64(mapVal)
|
afterEncode = convertor.ToStdBase64(mapVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
floatVal := 123.456
|
floatVal := 123.456
|
||||||
afterEncode = convertor.ToStdBase64(floatVal)
|
afterEncode = convertor.ToStdBase64(floatVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
boolVal := true
|
boolVal := true
|
||||||
afterEncode = convertor.ToStdBase64(boolVal)
|
afterEncode = convertor.ToStdBase64(boolVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
errVal := errors.New("err")
|
errVal := errors.New("err")
|
||||||
afterEncode = convertor.ToStdBase64(errVal)
|
afterEncode = convertor.ToStdBase64(errVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// aGVsbG8=
|
// aGVsbG8=
|
||||||
// aGVsbG8=
|
// aGVsbG8=
|
||||||
// MTIz
|
// MTIz
|
||||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||||
// MTIzLjQ1Ng==
|
// MTIzLjQ1Ng==
|
||||||
// dHJ1ZQ==
|
// dHJ1ZQ==
|
||||||
// ZXJy
|
// ZXJy
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -963,7 +963,7 @@ func main() {
|
|||||||
func ToUrlBase64(value any) string
|
func ToUrlBase64(value any) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/C_d0GlvEeUR)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -975,49 +975,49 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
afterEncode := convertor.ToUrlBase64(nil)
|
afterEncode := convertor.ToUrlBase64(nil)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
|
||||||
stringVal := "hello"
|
stringVal := "hello"
|
||||||
afterEncode = convertor.ToUrlBase64(stringVal)
|
afterEncode = convertor.ToUrlBase64(stringVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
byteSliceVal := []byte("hello")
|
byteSliceVal := []byte("hello")
|
||||||
afterEncode = convertor.ToUrlBase64(byteSliceVal)
|
afterEncode = convertor.ToUrlBase64(byteSliceVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
intVal := 123
|
intVal := 123
|
||||||
afterEncode = convertor.ToUrlBase64(intVal)
|
afterEncode = convertor.ToUrlBase64(intVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
A string
|
A string
|
||||||
B int
|
B int
|
||||||
}{"hello", 3}}
|
}{"hello", 3}}
|
||||||
afterEncode = convertor.ToUrlBase64(mapVal)
|
afterEncode = convertor.ToUrlBase64(mapVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
floatVal := 123.456
|
floatVal := 123.456
|
||||||
afterEncode = convertor.ToUrlBase64(floatVal)
|
afterEncode = convertor.ToUrlBase64(floatVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
boolVal := true
|
boolVal := true
|
||||||
afterEncode = convertor.ToUrlBase64(boolVal)
|
afterEncode = convertor.ToUrlBase64(boolVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
errVal := errors.New("err")
|
errVal := errors.New("err")
|
||||||
afterEncode = convertor.ToUrlBase64(errVal)
|
afterEncode = convertor.ToUrlBase64(errVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
//
|
//
|
||||||
// aGVsbG8=
|
// aGVsbG8=
|
||||||
// aGVsbG8=
|
// aGVsbG8=
|
||||||
// MTIz
|
// MTIz
|
||||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||||
// MTIzLjQ1Ng==
|
// MTIzLjQ1Ng==
|
||||||
// dHJ1ZQ==
|
// dHJ1ZQ==
|
||||||
// ZXJy
|
// ZXJy
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -1032,7 +1032,7 @@ func main() {
|
|||||||
func ToRawStdBase64(value any) string
|
func ToRawStdBase64(value any) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wSAr3sfkDcv)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -1044,45 +1044,45 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
stringVal := "hello"
|
stringVal := "hello"
|
||||||
afterEncode = convertor.ToRawStdBase64(stringVal)
|
afterEncode = convertor.ToRawStdBase64(stringVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
byteSliceVal := []byte("hello")
|
byteSliceVal := []byte("hello")
|
||||||
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
|
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
intVal := 123
|
intVal := 123
|
||||||
afterEncode = convertor.ToRawStdBase64(intVal)
|
afterEncode = convertor.ToRawStdBase64(intVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
A string
|
A string
|
||||||
B int
|
B int
|
||||||
}{"hello", 3}}
|
}{"hello", 3}}
|
||||||
afterEncode = convertor.ToRawStdBase64(mapVal)
|
afterEncode = convertor.ToRawStdBase64(mapVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
floatVal := 123.456
|
floatVal := 123.456
|
||||||
afterEncode = convertor.ToRawStdBase64(floatVal)
|
afterEncode := convertor.ToRawStdBase64(floatVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
boolVal := true
|
boolVal := true
|
||||||
afterEncode = convertor.ToRawStdBase64(boolVal)
|
afterEncode = convertor.ToRawStdBase64(boolVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
errVal := errors.New("err")
|
errVal := errors.New("err")
|
||||||
afterEncode = convertor.ToRawStdBase64(errVal)
|
afterEncode = convertor.ToRawStdBase64(errVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// aGVsbG8
|
// aGVsbG8
|
||||||
// aGVsbG8
|
// aGVsbG8
|
||||||
// MTIz
|
// MTIz
|
||||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||||
// MTIzLjQ1Ng
|
// MTIzLjQ1Ng
|
||||||
// dHJ1ZQ
|
// dHJ1ZQ
|
||||||
// ZXJy
|
// ZXJy
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1090,7 +1090,7 @@ func main() {
|
|||||||
|
|
||||||
<p>值转换为 ToRawUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
|
<p>值转换为 ToRawUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/HwdDPFcza1O)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func ToRawUrlBase64(value any) string
|
func ToRawUrlBase64(value any) string
|
||||||
@@ -1108,44 +1108,44 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
stringVal := "hello"
|
stringVal := "hello"
|
||||||
afterEncode = convertor.ToRawUrlBase64(stringVal)
|
afterEncode := convertor.ToRawUrlBase64(stringVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
byteSliceVal := []byte("hello")
|
byteSliceVal := []byte("hello")
|
||||||
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
|
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
intVal := 123
|
intVal := 123
|
||||||
afterEncode = convertor.ToRawUrlBase64(intVal)
|
afterEncode = convertor.ToRawUrlBase64(intVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
A string
|
A string
|
||||||
B int
|
B int
|
||||||
}{"hello", 3}}
|
}{"hello", 3}}
|
||||||
afterEncode = convertor.ToRawUrlBase64(mapVal)
|
afterEncode = convertor.ToRawUrlBase64(mapVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
floatVal := 123.456
|
floatVal := 123.456
|
||||||
afterEncode = convertor.ToRawUrlBase64(floatVal)
|
afterEncode = convertor.ToRawUrlBase64(floatVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
boolVal := true
|
boolVal := true
|
||||||
afterEncode = convertor.ToRawStdBase64(boolVal)
|
afterEncode = convertor.ToRawUrlBase64(boolVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
errVal := errors.New("err")
|
errVal := errors.New("err")
|
||||||
afterEncode = convertor.ToRawStdBase64(errVal)
|
afterEncode = convertor.ToRawUrlBase64(errVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// aGVsbG8
|
// aGVsbG8
|
||||||
// aGVsbG8
|
// aGVsbG8
|
||||||
// MTIz
|
// MTIz
|
||||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||||
// MTIzLjQ1Ng
|
// MTIzLjQ1Ng
|
||||||
// dHJ1ZQ
|
// dHJ1ZQ
|
||||||
// ZXJy
|
// ZXJy
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ import (
|
|||||||
- [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>
|
||||||
|
|
||||||
@@ -955,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
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -424,7 +424,7 @@ func longRunningTask() {
|
|||||||
func And[T any](predicates ...func(T) bool) func(T) bool
|
func And[T any](predicates ...func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dTBHJMQ0zD2)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -461,7 +461,7 @@ func main() {
|
|||||||
func Or[T any](predicates ...func(T) bool) func(T) bool
|
func Or[T any](predicates ...func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/LitCIsDFNDA)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -496,7 +496,7 @@ func main() {
|
|||||||
func Negate[T any](predicate func(T) bool) func(T) bool
|
func Negate[T any](predicate func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jbI8BtgFnVE)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -536,7 +536,7 @@ func main() {
|
|||||||
func Nor[T any](predicates ...func(T) bool) func(T) bool
|
func Nor[T any](predicates ...func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/2KdCoBEOq84)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -578,7 +578,7 @@ func main() {
|
|||||||
func Nand[T any](predicates ...func(T) bool) func(T) bool
|
func Nand[T any](predicates ...func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Rb-FdNGpgSO)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -615,7 +615,7 @@ func main() {
|
|||||||
func Xnor[T any](predicates ...func(T) bool) func(T) bool
|
func Xnor[T any](predicates ...func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/FJxko8SFbqc)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -652,7 +652,7 @@ func main() {
|
|||||||
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
|
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/XlXHHtzCf7d)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -663,9 +663,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
adder := function.AcceptIf(
|
||||||
adder := AcceptIf(
|
function.And(
|
||||||
And(
|
|
||||||
func(x int) bool {
|
func(x int) bool {
|
||||||
return x > 10
|
return x > 10
|
||||||
}, func(x int) bool {
|
}, func(x int) bool {
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ import (
|
|||||||
- [Minus](#Minus)
|
- [Minus](#Minus)
|
||||||
- [IsDisjoint](#IsDisjoint)
|
- [IsDisjoint](#IsDisjoint)
|
||||||
- [HasKey](#HasKey)
|
- [HasKey](#HasKey)
|
||||||
|
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
|
||||||
|
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
|
||||||
- [NewConcurrentMap](#NewConcurrentMap)
|
- [NewConcurrentMap](#NewConcurrentMap)
|
||||||
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
||||||
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
||||||
@@ -988,6 +990,98 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <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;">[运行](todo)</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;">[运行](todo)</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 := 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(keys2)
|
||||||
|
fmt.Println(values2)
|
||||||
|
|
||||||
|
fmt.Println(keys1)
|
||||||
|
fmt.Println(values1)
|
||||||
|
|
||||||
|
// 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]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="NewConcurrentMap">NewConcurrentMap</span>
|
### <span id="NewConcurrentMap">NewConcurrentMap</span>
|
||||||
|
|
||||||
<p>ConcurrentMap协程安全的map结构。</p>
|
<p>ConcurrentMap协程安全的map结构。</p>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ import (
|
|||||||
- [Join](#Join)
|
- [Join](#Join)
|
||||||
- [Partition](#Partition)
|
- [Partition](#Partition)
|
||||||
- [SetToDefaultIf](#SetToDefaultIf)
|
- [SetToDefaultIf](#SetToDefaultIf)
|
||||||
|
- [Break](#Break)
|
||||||
|
- [RightPadding](#RightPadding)
|
||||||
|
- [LeftPadding](#LeftPadding)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -2581,7 +2584,7 @@ func main() {
|
|||||||
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
|
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9AXGlPRC0-A)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
@@ -2600,4 +2603,91 @@ func main() {
|
|||||||
// [ b c d ]
|
// [ b c d ]
|
||||||
// 3
|
// 3
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Break">Break</span>
|
||||||
|
|
||||||
|
<p>根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Break[T any](values []T, predicate func(T) bool) ([]T, []T)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</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>示例:</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>示例:</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]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -62,6 +62,7 @@ import (
|
|||||||
- [RemoveWhiteSpace](#RemoveWhiteSpace)
|
- [RemoveWhiteSpace](#RemoveWhiteSpace)
|
||||||
- [SubInBetween](#SubInBetween)
|
- [SubInBetween](#SubInBetween)
|
||||||
- [HammingDistance](#HammingDistance)
|
- [HammingDistance](#HammingDistance)
|
||||||
|
- [Concat](#Concat)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -1475,7 +1476,7 @@ func main() {
|
|||||||
func SubInBetween(str string, start string, end string) string
|
func SubInBetween(str string, start string, end string) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/EDbaRvjeNsv)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
@@ -1505,10 +1506,10 @@ func main() {
|
|||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
HammingDistance(a, b string) (int, error)
|
func HammingDistance(a, b string) (int, error)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/glNdQEA9HUi)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
@@ -1528,4 +1529,38 @@ func main() {
|
|||||||
// 0
|
// 0
|
||||||
// 1
|
// 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
|
||||||
|
}
|
||||||
```
|
```
|
||||||
+209
-241
@@ -571,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>
|
||||||
@@ -610,7 +611,7 @@ func main() {
|
|||||||
func DecodeByte(data []byte, target any) error
|
func DecodeByte(data []byte, target any) error
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;"></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zI6xsmuQRbn)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -636,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>
|
||||||
|
|
||||||
@@ -779,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>
|
||||||
@@ -883,7 +786,7 @@ func main() {
|
|||||||
|
|
||||||
### <span id="ToStdBase64">ToStdBase64</span>
|
### <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>
|
<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>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -891,7 +794,7 @@ func main() {
|
|||||||
func ToStdBase64(value any) string
|
func ToStdBase64(value any) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/_fLJqJD3NMo)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -903,59 +806,61 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
afterEncode := convertor.ToStdBase64(nil)
|
afterEncode := convertor.ToStdBase64(nil)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
afterEncode = convertor.ToStdBase64("")
|
afterEncode = convertor.ToStdBase64("")
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
stringVal := "hello"
|
stringVal := "hello"
|
||||||
afterEncode = convertor.ToStdBase64(stringVal)
|
afterEncode = convertor.ToStdBase64(stringVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
byteSliceVal := []byte("hello")
|
byteSliceVal := []byte("hello")
|
||||||
afterEncode = convertor.ToStdBase64(byteSliceVal)
|
afterEncode = convertor.ToStdBase64(byteSliceVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
intVal := 123
|
intVal := 123
|
||||||
afterEncode = convertor.ToStdBase64(intVal)
|
afterEncode = convertor.ToStdBase64(intVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
A string
|
A string
|
||||||
B int
|
B int
|
||||||
}{"hello", 3}}
|
}{"hello", 3}}
|
||||||
afterEncode = convertor.ToStdBase64(mapVal)
|
afterEncode = convertor.ToStdBase64(mapVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
floatVal := 123.456
|
floatVal := 123.456
|
||||||
afterEncode = convertor.ToStdBase64(floatVal)
|
afterEncode = convertor.ToStdBase64(floatVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
boolVal := true
|
boolVal := true
|
||||||
afterEncode = convertor.ToStdBase64(boolVal)
|
afterEncode = convertor.ToStdBase64(boolVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
errVal := errors.New("err")
|
errVal := errors.New("err")
|
||||||
afterEncode = convertor.ToStdBase64(errVal)
|
afterEncode = convertor.ToStdBase64(errVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// aGVsbG8=
|
// aGVsbG8=
|
||||||
// aGVsbG8=
|
// aGVsbG8=
|
||||||
// MTIz
|
// MTIz
|
||||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||||
// MTIzLjQ1Ng==
|
// MTIzLjQ1Ng==
|
||||||
// dHJ1ZQ==
|
// dHJ1ZQ==
|
||||||
// ZXJy
|
// ZXJy
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### <span id="ToUrlBase64">ToUrlBase64</span>
|
### <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>
|
<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>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -963,7 +868,7 @@ func main() {
|
|||||||
func ToUrlBase64(value any) string
|
func ToUrlBase64(value any) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/C_d0GlvEeUR)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -975,56 +880,56 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
afterEncode := convertor.ToUrlBase64(nil)
|
afterEncode := convertor.ToUrlBase64(nil)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
|
|
||||||
stringVal := "hello"
|
stringVal := "hello"
|
||||||
afterEncode = convertor.ToUrlBase64(stringVal)
|
afterEncode = convertor.ToUrlBase64(stringVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
byteSliceVal := []byte("hello")
|
byteSliceVal := []byte("hello")
|
||||||
afterEncode = convertor.ToUrlBase64(byteSliceVal)
|
afterEncode = convertor.ToUrlBase64(byteSliceVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
intVal := 123
|
intVal := 123
|
||||||
afterEncode = convertor.ToUrlBase64(intVal)
|
afterEncode = convertor.ToUrlBase64(intVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
A string
|
A string
|
||||||
B int
|
B int
|
||||||
}{"hello", 3}}
|
}{"hello", 3}}
|
||||||
afterEncode = convertor.ToUrlBase64(mapVal)
|
afterEncode = convertor.ToUrlBase64(mapVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
floatVal := 123.456
|
floatVal := 123.456
|
||||||
afterEncode = convertor.ToUrlBase64(floatVal)
|
afterEncode = convertor.ToUrlBase64(floatVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
boolVal := true
|
boolVal := true
|
||||||
afterEncode = convertor.ToUrlBase64(boolVal)
|
afterEncode = convertor.ToUrlBase64(boolVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
errVal := errors.New("err")
|
errVal := errors.New("err")
|
||||||
afterEncode = convertor.ToUrlBase64(errVal)
|
afterEncode = convertor.ToUrlBase64(errVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
//
|
//
|
||||||
// aGVsbG8=
|
// aGVsbG8=
|
||||||
// aGVsbG8=
|
// aGVsbG8=
|
||||||
// MTIz
|
// MTIz
|
||||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||||
// MTIzLjQ1Ng==
|
// MTIzLjQ1Ng==
|
||||||
// dHJ1ZQ==
|
// dHJ1ZQ==
|
||||||
// ZXJy
|
// ZXJy
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="ToRawStdBase64">ToRawStdBase64</span>
|
### <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>
|
<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>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -1032,7 +937,7 @@ func main() {
|
|||||||
func ToRawStdBase64(value any) string
|
func ToRawStdBase64(value any) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wSAr3sfkDcv)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -1044,51 +949,51 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
stringVal := "hello"
|
stringVal := "hello"
|
||||||
afterEncode = convertor.ToRawStdBase64(stringVal)
|
afterEncode := convertor.ToRawStdBase64(stringVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
byteSliceVal := []byte("hello")
|
byteSliceVal := []byte("hello")
|
||||||
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
|
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
intVal := 123
|
intVal := 123
|
||||||
afterEncode = convertor.ToRawStdBase64(intVal)
|
afterEncode = convertor.ToRawStdBase64(intVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
A string
|
A string
|
||||||
B int
|
B int
|
||||||
}{"hello", 3}}
|
}{"hello", 3}}
|
||||||
afterEncode = convertor.ToRawStdBase64(mapVal)
|
afterEncode = convertor.ToRawStdBase64(mapVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
floatVal := 123.456
|
floatVal := 123.456
|
||||||
afterEncode = convertor.ToRawStdBase64(floatVal)
|
afterEncode = convertor.ToRawStdBase64(floatVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
boolVal := true
|
boolVal := true
|
||||||
afterEncode = convertor.ToRawStdBase64(boolVal)
|
afterEncode = convertor.ToRawStdBase64(boolVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
errVal := errors.New("err")
|
errVal := errors.New("err")
|
||||||
afterEncode = convertor.ToRawStdBase64(errVal)
|
afterEncode = convertor.ToRawStdBase64(errVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// aGVsbG8
|
// aGVsbG8
|
||||||
// aGVsbG8
|
// aGVsbG8
|
||||||
// MTIz
|
// MTIz
|
||||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||||
// MTIzLjQ1Ng
|
// MTIzLjQ1Ng
|
||||||
// dHJ1ZQ
|
// dHJ1ZQ
|
||||||
// ZXJy
|
// ZXJy
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="ToRawUrlBase64">ToRawUrlBase64</span>
|
### <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>
|
<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>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -1096,7 +1001,7 @@ func main() {
|
|||||||
func ToRawUrlBase64(value any) string
|
func ToRawUrlBase64(value any) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/HwdDPFcza1O)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -1108,44 +1013,107 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
stringVal := "hello"
|
stringVal := "hello"
|
||||||
afterEncode = convertor.ToRawUrlBase64(stringVal)
|
afterEncode := convertor.ToRawUrlBase64(stringVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
byteSliceVal := []byte("hello")
|
byteSliceVal := []byte("hello")
|
||||||
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
|
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
intVal := 123
|
intVal := 123
|
||||||
afterEncode = convertor.ToRawUrlBase64(intVal)
|
afterEncode = convertor.ToRawUrlBase64(intVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||||
A string
|
A string
|
||||||
B int
|
B int
|
||||||
}{"hello", 3}}
|
}{"hello", 3}}
|
||||||
afterEncode = convertor.ToRawUrlBase64(mapVal)
|
afterEncode = convertor.ToRawUrlBase64(mapVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
floatVal := 123.456
|
floatVal := 123.456
|
||||||
afterEncode = convertor.ToRawUrlBase64(floatVal)
|
afterEncode = convertor.ToRawUrlBase64(floatVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
boolVal := true
|
boolVal := true
|
||||||
afterEncode = convertor.ToRawStdBase64(boolVal)
|
afterEncode = convertor.ToRawUrlBase64(boolVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
errVal := errors.New("err")
|
errVal := errors.New("err")
|
||||||
afterEncode = convertor.ToRawStdBase64(errVal)
|
afterEncode = convertor.ToRawUrlBase64(errVal)
|
||||||
fmt.Println(afterEncode)
|
fmt.Println(afterEncode)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// aGVsbG8
|
// aGVsbG8
|
||||||
// aGVsbG8
|
// aGVsbG8
|
||||||
// MTIz
|
// MTIz
|
||||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||||
// MTIzLjQ1Ng
|
// MTIzLjQ1Ng
|
||||||
// dHJ1ZQ
|
// dHJ1ZQ
|
||||||
// ZXJy
|
// 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
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -50,6 +50,8 @@ import (
|
|||||||
- [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>
|
||||||
|
|
||||||
@@ -165,7 +167,7 @@ func main() {
|
|||||||
|
|
||||||
### <span id="CopyDir">CopyDir</span>
|
### <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>
|
<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>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -961,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
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -423,7 +423,7 @@ func longRunningTask() {
|
|||||||
func And[T any](predicates ...func(T) bool) func(T) bool
|
func And[T any](predicates ...func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/dTBHJMQ0zD2)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -460,7 +460,7 @@ func main() {
|
|||||||
func Or[T any](predicates ...func(T) bool) func(T) bool
|
func Or[T any](predicates ...func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/LitCIsDFNDA)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -495,7 +495,7 @@ func main() {
|
|||||||
func Negate[T any](predicate func(T) bool) func(T) bool
|
func Negate[T any](predicate func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jbI8BtgFnVE)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -535,7 +535,7 @@ func main() {
|
|||||||
func Nor[T any](predicates ...func(T) bool) func(T) bool
|
func Nor[T any](predicates ...func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/2KdCoBEOq84)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -577,7 +577,7 @@ func main() {
|
|||||||
func Nand[T any](predicates ...func(T) bool) func(T) bool
|
func Nand[T any](predicates ...func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Rb-FdNGpgSO)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -614,7 +614,7 @@ func main() {
|
|||||||
func Xnor[T any](predicates ...func(T) bool) func(T) bool
|
func Xnor[T any](predicates ...func(T) bool) func(T) bool
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/FJxko8SFbqc)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -643,9 +643,7 @@ func main() {
|
|||||||
|
|
||||||
### <span id="AcceptIf">AcceptIf</span>
|
### <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.
|
<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>
|
||||||
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>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -653,7 +651,7 @@ An apply function that also takes an argument of type T and returns a modified v
|
|||||||
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
|
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/XlXHHtzCf7d)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -665,8 +663,8 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
adder := AcceptIf(
|
adder := function.AcceptIf(
|
||||||
And(
|
function.And(
|
||||||
func(x int) bool {
|
func(x int) bool {
|
||||||
return x > 10
|
return x > 10
|
||||||
}, func(x int) bool {
|
}, func(x int) bool {
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ import (
|
|||||||
- [Minus](#Minus)
|
- [Minus](#Minus)
|
||||||
- [IsDisjoint](#IsDisjoint)
|
- [IsDisjoint](#IsDisjoint)
|
||||||
- [HasKey](#HasKey)
|
- [HasKey](#HasKey)
|
||||||
|
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
|
||||||
|
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
|
||||||
- [NewConcurrentMap](#NewConcurrentMap)
|
- [NewConcurrentMap](#NewConcurrentMap)
|
||||||
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
||||||
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
||||||
@@ -992,6 +994,101 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <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](Todo)</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>
|
||||||
|
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](Todo)</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 := 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(keys2)
|
||||||
|
fmt.Println(values2)
|
||||||
|
|
||||||
|
fmt.Println(keys1)
|
||||||
|
fmt.Println(values1)
|
||||||
|
|
||||||
|
// 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]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -93,6 +93,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>
|
||||||
|
|
||||||
@@ -2577,7 +2581,7 @@ func main() {
|
|||||||
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
|
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/9AXGlPRC0-A)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
@@ -2596,4 +2600,91 @@ func main() {
|
|||||||
// [ b c d ]
|
// [ b c d ]
|
||||||
// 3
|
// 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:</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:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
padded := RightPadding(nums, 0, 3)
|
||||||
|
fmt.Println(padded)
|
||||||
|
// Output:
|
||||||
|
// [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:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
padded := LeftPadding(nums, 0, 3)
|
||||||
|
fmt.Println(padded)
|
||||||
|
// Output:
|
||||||
|
// [0 0 0 1 2 3 4 5]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -62,6 +62,7 @@ import (
|
|||||||
- [RemoveWhiteSpace](#RemoveWhiteSpace)
|
- [RemoveWhiteSpace](#RemoveWhiteSpace)
|
||||||
- [SubInBetween](#SubInBetween)
|
- [SubInBetween](#SubInBetween)
|
||||||
- [HammingDistance](#HammingDistance)
|
- [HammingDistance](#HammingDistance)
|
||||||
|
- [Concat](#Concat)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -1477,7 +1478,7 @@ func main() {
|
|||||||
func SubInBetween(str string, start string, end string) string
|
func SubInBetween(str string, start string, end string) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/EDbaRvjeNsv)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
@@ -1507,10 +1508,10 @@ func main() {
|
|||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
HammingDistance(a, b string) (int, error)
|
func HammingDistance(a, b string) (int, error)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/glNdQEA9HUi)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
@@ -1530,4 +1531,39 @@ func main() {
|
|||||||
// 0
|
// 0
|
||||||
// 1
|
// 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.
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代
|
|||||||
|
|
||||||
- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。
|
- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。
|
||||||
|
|
||||||
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
|
- 确保 PR 是提交到 `rc` 分支,而不是其他分支。
|
||||||
|
|
||||||
- 如果是修复 bug,请在 PR 中给出描述信息。
|
- 如果是修复 bug,请在 PR 中给出描述信息。
|
||||||
|
|
||||||
|
|||||||
+30
-22
@@ -16,7 +16,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -869,12 +868,13 @@ func isCsvSupportedType(v interface{}) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChunkRead 从文件的指定偏移读取块并返回块内所有行
|
// ChunkRead reads a block from the file at the specified offset and returns all lines within the block
|
||||||
func ChunkRead(f *os.File, offset int64, size int, bufPool *sync.Pool) []string {
|
// 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获取缓冲区并调整大小
|
buf := bufPool.Get().([]byte)[:size] // 从Pool获取缓冲区并调整大小
|
||||||
n, err := f.ReadAt(buf, offset) // 从指定偏移读取数据到缓冲区
|
n, err := file.ReadAt(buf, offset) // 从指定偏移读取数据到缓冲区
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
buf = buf[:n] // 调整切片以匹配实际读取的字节数
|
buf = buf[:n] // 调整切片以匹配实际读取的字节数
|
||||||
|
|
||||||
@@ -893,58 +893,64 @@ func ChunkRead(f *os.File, offset int64, size int, bufPool *sync.Pool) []string
|
|||||||
lines = append(lines, line)
|
lines = append(lines, line)
|
||||||
}
|
}
|
||||||
bufPool.Put(buf) // 读取完成后,将缓冲区放回Pool
|
bufPool.Put(buf) // 读取完成后,将缓冲区放回Pool
|
||||||
return lines
|
return lines, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 并行读取文件并将每个块的行发送到指定通道
|
// ParallelChunkRead reads the file in parallel and send each chunk of lines to the specified channel.
|
||||||
// filePath 文件路径
|
// filePath 文件路径
|
||||||
// ChunkSizeMB 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整
|
// chunkSizeMB 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整
|
||||||
// MaxGoroutine 并发读取分块的数量,设置为0时使用CPU核心数
|
// maxGoroutine 并发读取分块的数量,设置为0时使用CPU核心数
|
||||||
// linesCh用于接收返回结果的通道。
|
// linesCh用于接收返回结果的通道。
|
||||||
func ParallelChunkRead(filePath string, linesCh chan<- []string, ChunkSizeMB, MaxGoroutine int) {
|
// Play: https://go.dev/play/p/teMXnCsdSEw
|
||||||
if ChunkSizeMB == 0 {
|
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error {
|
||||||
ChunkSizeMB = 100
|
if chunkSizeMB == 0 {
|
||||||
|
chunkSizeMB = 100
|
||||||
}
|
}
|
||||||
ChunkSize := ChunkSizeMB * 1024 * 1024
|
chunkSize := chunkSizeMB * 1024 * 1024
|
||||||
// 内存复用
|
// 内存复用
|
||||||
bufPool := sync.Pool{
|
bufPool := sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() interface{} {
|
||||||
return make([]byte, 0, ChunkSize)
|
return make([]byte, 0, chunkSize)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if MaxGoroutine == 0 {
|
if maxGoroutine == 0 {
|
||||||
MaxGoroutine = runtime.NumCPU() // 设置为0时使用CPU核心数
|
maxGoroutine = runtime.NumCPU() // 设置为0时使用CPU核心数
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.Open(filePath)
|
f, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to open file: %v", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
info, err := f.Stat()
|
info, err := f.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to get file info: %v", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
chunkOffsetCh := make(chan int64, MaxGoroutine)
|
chunkOffsetCh := make(chan int64, maxGoroutine)
|
||||||
|
|
||||||
// 分配工作
|
// 分配工作
|
||||||
go func() {
|
go func() {
|
||||||
for i := int64(0); i < info.Size(); i += int64(ChunkSize) {
|
for i := int64(0); i < info.Size(); i += int64(chunkSize) {
|
||||||
chunkOffsetCh <- i
|
chunkOffsetCh <- i
|
||||||
}
|
}
|
||||||
close(chunkOffsetCh)
|
close(chunkOffsetCh)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 启动工作协程
|
// 启动工作协程
|
||||||
for i := 0; i < MaxGoroutine; i++ {
|
for i := 0; i < maxGoroutine; i++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
for chunkOffset := range chunkOffsetCh {
|
for chunkOffset := range chunkOffsetCh {
|
||||||
linesCh <- ChunkRead(f, chunkOffset, ChunkSize, &bufPool)
|
chunk, err := ChunkRead(f, chunkOffset, chunkSize, &bufPool)
|
||||||
|
if err == nil {
|
||||||
|
linesCh <- chunk
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
@@ -953,4 +959,6 @@ func ParallelChunkRead(filePath string, linesCh chan<- []string, ChunkSizeMB, Ma
|
|||||||
// 等待所有解析完成后关闭行通道
|
// 等待所有解析完成后关闭行通道
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(linesCh)
|
close(linesCh)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleIsExist() {
|
func ExampleIsExist() {
|
||||||
@@ -421,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
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
@@ -566,3 +568,54 @@ func TestCopyDir(t *testing.T) {
|
|||||||
|
|
||||||
os.RemoveAll(dest)
|
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])
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Vendored
-3
@@ -1,5 +1,2 @@
|
|||||||
Lili,22,female
|
Lili,22,female
|
||||||
Jim,21,male
|
Jim,21,male
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
@@ -139,6 +139,7 @@ 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.
|
// 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.
|
// 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.
|
// 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) {
|
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool) {
|
||||||
if predicate == nil {
|
if predicate == nil {
|
||||||
panic("programming error: predicate must be not nil")
|
panic("programming error: predicate must be not nil")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package function
|
|||||||
|
|
||||||
// And returns a composed predicate that represents the logical AND of a list of predicates.
|
// 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.
|
// 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 {
|
func And[T any](predicates ...func(T) bool) func(T) bool {
|
||||||
if len(predicates) < 2 {
|
if len(predicates) < 2 {
|
||||||
panic("programming error: predicates count must be at least 2")
|
panic("programming error: predicates count must be at least 2")
|
||||||
@@ -18,6 +19,7 @@ func And[T any](predicates ...func(T) bool) func(T) bool {
|
|||||||
|
|
||||||
// Nand returns a composed predicate that represents the logical NAND of a list of predicates.
|
// 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.
|
// 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 {
|
func Nand[T any](predicates ...func(T) bool) func(T) bool {
|
||||||
if len(predicates) < 2 {
|
if len(predicates) < 2 {
|
||||||
panic("programming error: predicates count must be at least 2")
|
panic("programming error: predicates count must be at least 2")
|
||||||
@@ -33,6 +35,7 @@ func Nand[T any](predicates ...func(T) bool) func(T) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Negate returns a predicate that represents the logical negation of this predicate.
|
// 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 {
|
func Negate[T any](predicate func(T) bool) func(T) bool {
|
||||||
return func(value T) bool {
|
return func(value T) bool {
|
||||||
return !predicate(value)
|
return !predicate(value)
|
||||||
@@ -41,6 +44,7 @@ func Negate[T any](predicate func(T) bool) func(T) bool {
|
|||||||
|
|
||||||
// Or returns a composed predicate that represents the logical OR of a list of predicates.
|
// 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.
|
// 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 {
|
func Or[T any](predicates ...func(T) bool) func(T) bool {
|
||||||
if len(predicates) < 2 {
|
if len(predicates) < 2 {
|
||||||
panic("programming error: predicates count must be at least 2")
|
panic("programming error: predicates count must be at least 2")
|
||||||
@@ -57,6 +61,7 @@ func Or[T any](predicates ...func(T) bool) func(T) bool {
|
|||||||
|
|
||||||
// Nor returns a composed predicate that represents the logical NOR of a list of predicates.
|
// 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.
|
// 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 {
|
func Nor[T any](predicates ...func(T) bool) func(T) bool {
|
||||||
if len(predicates) < 2 {
|
if len(predicates) < 2 {
|
||||||
panic("programming error: predicates count must be at least 2")
|
panic("programming error: predicates count must be at least 2")
|
||||||
@@ -73,6 +78,7 @@ func Nor[T any](predicates ...func(T) bool) func(T) bool {
|
|||||||
|
|
||||||
// Xnor returns a composed predicate that represents the logical XNOR of a list of predicates.
|
// 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.
|
// 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 {
|
func Xnor[T any](predicates ...func(T) bool) func(T) bool {
|
||||||
if len(predicates) < 2 {
|
if len(predicates) < 2 {
|
||||||
panic("programming error: predicates count must be at least 2")
|
panic("programming error: predicates count must be at least 2")
|
||||||
|
|||||||
+52
-2
@@ -7,6 +7,10 @@ package maputil
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/slice"
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
)
|
)
|
||||||
@@ -375,8 +379,7 @@ func getFieldNameByJsonTag(structObj any, jsonTag string) string {
|
|||||||
for i := 0; i < s.NumField(); i++ {
|
for i := 0; i < s.NumField(); i++ {
|
||||||
field := s.Field(i)
|
field := s.Field(i)
|
||||||
tag := field.Tag
|
tag := field.Tag
|
||||||
name := tag.Get("json")
|
name, _, _ := strings.Cut(tag.Get("json"), ",")
|
||||||
|
|
||||||
if name == jsonTag {
|
if name == jsonTag {
|
||||||
return field.Name
|
return field.Name
|
||||||
}
|
}
|
||||||
@@ -384,3 +387,50 @@ func getFieldNameByJsonTag(structObj any, jsonTag string) string {
|
|||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToSortedSlicesDefault converts a map to two slices sorted by key: one for the keys and another for the values.
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleKeys() {
|
func ExampleKeys() {
|
||||||
@@ -450,3 +451,76 @@ 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]
|
||||||
|
}
|
||||||
|
|||||||
+181
-1
@@ -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"
|
||||||
)
|
)
|
||||||
@@ -483,7 +485,7 @@ func TestMapToStruct(t *testing.T) {
|
|||||||
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"`
|
Addr *Address `json:"address,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
Address struct {
|
Address struct {
|
||||||
@@ -511,3 +513,181 @@ func TestMapToStruct(t *testing.T) {
|
|||||||
assert.Equal("test", p.Addr.Street)
|
assert.Equal("test", p.Addr.Street)
|
||||||
assert.Equal(1, p.Addr.Number)
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+47
-3
@@ -66,7 +66,7 @@ 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[T constraints.Float | constraints.Integer](x T, 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))
|
||||||
@@ -76,7 +76,7 @@ func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string
|
|||||||
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[T constraints.Float | constraints.Integer](x T, 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))
|
||||||
@@ -100,6 +100,44 @@ func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T {
|
|||||||
return T(result)
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// Max return max value of numbers.
|
// Max return max value of numbers.
|
||||||
// Play: https://go.dev/play/p/cN8DHI0rTkH
|
// Play: https://go.dev/play/p/cN8DHI0rTkH
|
||||||
func Max[T constraints.Integer | constraints.Float](numbers ...T) T {
|
func Max[T constraints.Integer | constraints.Float](numbers ...T) T {
|
||||||
@@ -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,8 +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("54.321", RoundToString(54.321, 3))
|
||||||
//assert.Equal("17.000", RoundToString(17, 3))
|
assert.Equal("17.000", RoundToString(17, 3))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTruncRound(t *testing.T) {
|
func TestTruncRound(t *testing.T) {
|
||||||
@@ -93,6 +93,51 @@ func TestTruncRound(t *testing.T) {
|
|||||||
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()
|
||||||
|
|
||||||
@@ -352,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)))
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-3
@@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -109,6 +108,7 @@ type HttpClientConfig struct {
|
|||||||
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.
|
||||||
@@ -164,6 +164,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 +368,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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+63
-30
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+3
-3
@@ -45,7 +45,7 @@ func RetryTimes(n uint) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RetryWithCustomBackoff set abitary custom backoff strategy
|
// RetryWithCustomBackoff set abitary custom backoff strategy
|
||||||
// todo: Add playground link
|
// Play: todo
|
||||||
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
|
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
|
||||||
if backoffStrategy == nil {
|
if backoffStrategy == nil {
|
||||||
panic("programming error: backoffStrategy must be not nil")
|
panic("programming error: backoffStrategy must be not nil")
|
||||||
@@ -57,7 +57,7 @@ func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RetryWithLinearBackoff set linear strategy backoff
|
// RetryWithLinearBackoff set linear strategy backoff
|
||||||
// todo: Add playground link
|
// Play: todo
|
||||||
func RetryWithLinearBackoff(interval time.Duration) Option {
|
func RetryWithLinearBackoff(interval time.Duration) Option {
|
||||||
if interval <= 0 {
|
if interval <= 0 {
|
||||||
panic("programming error: retry interval should not be lower or equal to 0")
|
panic("programming error: retry interval should not be lower or equal to 0")
|
||||||
@@ -71,7 +71,7 @@ func RetryWithLinearBackoff(interval time.Duration) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RetryWithExponentialWithJitterBackoff set exponential strategy backoff
|
// RetryWithExponentialWithJitterBackoff set exponential strategy backoff
|
||||||
// todo: Add playground link
|
// Play: todo
|
||||||
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option {
|
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option {
|
||||||
if interval <= 0 {
|
if interval <= 0 {
|
||||||
panic("programming error: retry interval should not be lower or equal to 0")
|
panic("programming error: retry interval should not be lower or equal to 0")
|
||||||
|
|||||||
+95
-24
@@ -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,35 +92,41 @@ 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](slice []T, slices ...[]T) []T {
|
||||||
result := append([]T{}, slice...)
|
totalLen := len(slice)
|
||||||
|
|
||||||
for _, v := range slices {
|
for _, v := range slices {
|
||||||
result = append(result, v...)
|
totalLen += len(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, 0, totalLen)
|
||||||
|
|
||||||
|
result = append(result, slice...)
|
||||||
|
for _, s := range slices {
|
||||||
|
result = append(result, s...)
|
||||||
}
|
}
|
||||||
|
|
||||||
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{}
|
||||||
@@ -755,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -826,7 +836,11 @@ func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T {
|
|||||||
// 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)
|
totalLen := 0
|
||||||
|
for _, v := range slices {
|
||||||
|
totalLen += len(v)
|
||||||
|
}
|
||||||
|
result := make([]T, 0, totalLen)
|
||||||
|
|
||||||
for _, v := range slices {
|
for _, v := range slices {
|
||||||
result = append(result, v...)
|
result = append(result, v...)
|
||||||
@@ -1173,6 +1187,7 @@ func AppendIfAbsent[T comparable](slice []T, item T) []T {
|
|||||||
// SetToDefaultIf sets elements to their default value if they match the given predicate.
|
// SetToDefaultIf sets elements to their default value if they match the given predicate.
|
||||||
// It retains the positions of the elements in the slice.
|
// It retains the positions of the elements in the slice.
|
||||||
// It returns slice of T and the count of modified slice items
|
// 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) {
|
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int) {
|
||||||
var count int
|
var count int
|
||||||
for i := 0; i < len(slice); i++ {
|
for i := 0; i < len(slice); i++ {
|
||||||
@@ -1238,6 +1253,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: Todo
|
||||||
|
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) {
|
||||||
@@ -1248,3 +1287,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: Todo
|
||||||
|
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: Todo
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1112,3 +1112,32 @@ func ExampleSetToDefaultIf() {
|
|||||||
// [ b c d ]
|
// [ b c d ]
|
||||||
// 3
|
// 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]
|
||||||
|
}
|
||||||
|
|||||||
+65
-2
@@ -2,12 +2,11 @@ package slice
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContain(t *testing.T) {
|
func TestContain(t *testing.T) {
|
||||||
@@ -109,6 +108,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()
|
||||||
|
|
||||||
@@ -1357,3 +1369,54 @@ func TestSetToDefaultIf(t *testing.T) {
|
|||||||
assert.Equal(2, count)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
+23
-1
@@ -585,7 +585,7 @@ func RemoveWhiteSpace(str string, repalceAll bool) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SubInBetween return substring between the start and end position(excluded) of source string.
|
// SubInBetween return substring between the start and end position(excluded) of source string.
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/EDbaRvjeNsv
|
||||||
func SubInBetween(str string, start string, end string) string {
|
func SubInBetween(str string, start string, end string) string {
|
||||||
if _, after, ok := strings.Cut(str, start); ok {
|
if _, after, ok := strings.Cut(str, start); ok {
|
||||||
if before, _, ok := strings.Cut(after, end); ok {
|
if before, _, ok := strings.Cut(after, end); ok {
|
||||||
@@ -599,6 +599,7 @@ func SubInBetween(str string, start string, end string) string {
|
|||||||
// HammingDistance calculates the Hamming distance between two strings.
|
// HammingDistance calculates the Hamming distance between two strings.
|
||||||
// The Hamming distance is the number of positions at which the corresponding symbols are different.
|
// 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.
|
// 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) {
|
func HammingDistance(a, b string) (int, error) {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return -1, errors.New("a length and b length are unequal")
|
return -1, errors.New("a length and b length are unequal")
|
||||||
@@ -616,3 +617,24 @@ func HammingDistance(a, b string) (int, error) {
|
|||||||
|
|
||||||
return distance, nil
|
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()
|
||||||
|
}
|
||||||
|
|||||||
@@ -680,3 +680,17 @@ func ExampleHammingDistance() {
|
|||||||
// 3
|
// 3
|
||||||
// 1
|
// 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
|
||||||
|
}
|
||||||
|
|||||||
@@ -561,6 +561,7 @@ func TestContainsAny(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoveWhiteSpace(t *testing.T) {
|
func TestRemoveWhiteSpace(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
assert := internal.NewAssert(t, "TestRemoveWhiteSpace")
|
assert := internal.NewAssert(t, "TestRemoveWhiteSpace")
|
||||||
|
|
||||||
str := " hello \r\n \t world"
|
str := " hello \r\n \t world"
|
||||||
@@ -571,6 +572,7 @@ func TestRemoveWhiteSpace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSubInBetween(t *testing.T) {
|
func TestSubInBetween(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
assert := internal.NewAssert(t, "TestSubInBetween")
|
assert := internal.NewAssert(t, "TestSubInBetween")
|
||||||
|
|
||||||
str := "abcde"
|
str := "abcde"
|
||||||
@@ -583,6 +585,7 @@ func TestSubInBetween(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHammingDistance(t *testing.T) {
|
func TestHammingDistance(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
assert := internal.NewAssert(t, "HammingDistance")
|
assert := internal.NewAssert(t, "HammingDistance")
|
||||||
|
|
||||||
hd := func(a, b string) int {
|
hd := func(a, b string) int {
|
||||||
@@ -604,3 +607,16 @@ func TestHammingDistance(t *testing.T) {
|
|||||||
assert.Equal(0, hd("日本語", "日本語"))
|
assert.Equal(0, hd("日本語", "日本語"))
|
||||||
assert.Equal(3, hd("日本語", "語日本"))
|
assert.Equal(3, hd("日本語", "語日本"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConcat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestConcat")
|
||||||
|
|
||||||
|
assert.Equal("", Concat(0))
|
||||||
|
assert.Equal("a", Concat(1, "a"))
|
||||||
|
assert.Equal("ab", Concat(2, "a", "b"))
|
||||||
|
assert.Equal("abc", Concat(3, "a", "b", "c"))
|
||||||
|
assert.Equal("abc", Concat(3, "a", "", "b", "c", ""))
|
||||||
|
assert.Equal("你好,世界!", Concat(0, "你好", ",", "", "世界!", ""))
|
||||||
|
assert.Equal("Hello World!", Concat(0, "Hello", " Wo", "r", "ld!", ""))
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/mail"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -24,7 +25,7 @@ var (
|
|||||||
intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
|
intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
|
||||||
urlMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
|
urlMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
|
||||||
dnsMatcher *regexp.Regexp = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`)
|
dnsMatcher *regexp.Regexp = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`)
|
||||||
emailMatcher *regexp.Regexp = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`)
|
emailMatcher *regexp.Regexp = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
|
||||||
chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`)
|
chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`)
|
||||||
chineseIdMatcher *regexp.Regexp = regexp.MustCompile(`^(\d{17})([0-9]|X|x)$`)
|
chineseIdMatcher *regexp.Regexp = regexp.MustCompile(`^(\d{17})([0-9]|X|x)$`)
|
||||||
chineseMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
|
chineseMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
|
||||||
@@ -264,7 +265,10 @@ func IsDns(dns string) bool {
|
|||||||
// IsEmail check if the string is a email address.
|
// IsEmail check if the string is a email address.
|
||||||
// Play: https://go.dev/play/p/Os9VaFlT33G
|
// Play: https://go.dev/play/p/Os9VaFlT33G
|
||||||
func IsEmail(email string) bool {
|
func IsEmail(email string) bool {
|
||||||
return emailMatcher.MatchString(email)
|
_, err := mail.ParseAddress(email)
|
||||||
|
return err == nil
|
||||||
|
|
||||||
|
// return emailMatcher.MatchString(email)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsChineseMobile check if the string is chinese mobile number.
|
// IsChineseMobile check if the string is chinese mobile number.
|
||||||
|
|||||||
@@ -285,6 +285,7 @@ func TestIsEmail(t *testing.T) {
|
|||||||
assert := internal.NewAssert(t, "TestIsEmail")
|
assert := internal.NewAssert(t, "TestIsEmail")
|
||||||
|
|
||||||
assert.Equal(true, IsEmail("abc@xyz.com"))
|
assert.Equal(true, IsEmail("abc@xyz.com"))
|
||||||
|
assert.Equal(false, IsEmail("@abc@xyz.com"))
|
||||||
assert.Equal(false, IsEmail("a.b@@com"))
|
assert.Equal(false, IsEmail("a.b@@com"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user