mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-15 02:02:27 +08:00
Compare commits
74 Commits
v2.3.0
...
ec092a009a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec092a009a | ||
|
|
ca40b5d6c6 | ||
|
|
a6d39a3bba | ||
|
|
38148978cf | ||
|
|
3e8c3bd396 | ||
|
|
30971c1aab | ||
|
|
bc260277bc | ||
|
|
c0b200f846 | ||
|
|
305847993c | ||
|
|
f5d70728c3 | ||
|
|
c2a5335bc6 | ||
|
|
7b4e060f85 | ||
|
|
a360372aa9 | ||
|
|
7f78a6b11e | ||
|
|
5c53cb5867 | ||
|
|
f7e9d5dc47 | ||
|
|
5c580ed013 | ||
|
|
0f9764f41e | ||
|
|
0bc5f82554 | ||
|
|
e91965b013 | ||
|
|
483a286d8e | ||
|
|
3f8e306ced | ||
|
|
0b29f0520d | ||
|
|
8611ec0c10 | ||
|
|
286e10d189 | ||
|
|
3e7f94b03e | ||
|
|
356351896d | ||
|
|
9be124211e | ||
|
|
f467658481 | ||
|
|
5c9d0e396e | ||
|
|
8f74460c1b | ||
|
|
d5752499bf | ||
|
|
eb7cf76eae | ||
|
|
4af074d181 | ||
|
|
73fb8fefd2 | ||
|
|
9cf535055d | ||
|
|
8be7b3e396 | ||
|
|
dac706d700 | ||
|
|
2097277a7d | ||
|
|
95b516e278 | ||
|
|
ca373b00a7 | ||
|
|
a220220f09 | ||
|
|
aeef0418a4 | ||
|
|
9b7d8d7abf | ||
|
|
4d21e81263 | ||
|
|
ce2397422e | ||
|
|
4b3a62b36a | ||
|
|
e054680d20 | ||
|
|
5381842eec | ||
|
|
6e0498514c | ||
|
|
967e6a3493 | ||
|
|
5b24801e49 | ||
|
|
974ba525a6 | ||
|
|
f0235c40b6 | ||
|
|
712a215ea6 | ||
|
|
7893f828d3 | ||
|
|
53fa210f09 | ||
|
|
de9ee08be4 | ||
|
|
5381450bea | ||
|
|
6853d627f4 | ||
|
|
e461acdb72 | ||
|
|
2a796adf85 | ||
|
|
5e6e8d82a8 | ||
|
|
e9280b8c25 | ||
|
|
bb6f10a1fb | ||
|
|
33b4cffe60 | ||
|
|
2b765b49e0 | ||
|
|
004dbdc32e | ||
|
|
ab50e8120a | ||
|
|
73c97af7d8 | ||
|
|
5e8a065eaa | ||
|
|
aa74400607 | ||
|
|
a6eaaef563 | ||
|
|
1b31014f81 |
@@ -1,4 +1,4 @@
|
|||||||
# Lancet Contributing Guide
|
# Lancet Contribution Guide
|
||||||
|
|
||||||
Hi! Thank you for choosing Lancet.
|
Hi! Thank you for choosing Lancet.
|
||||||
|
|
||||||
75
README.md
75
README.md
@@ -4,7 +4,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||

|

|
||||||
[](https://github.com/duke-git/lancet/releases)
|
[](https://github.com/duke-git/lancet/releases)
|
||||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||||
@@ -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)]
|
||||||
@@ -731,8 +734,10 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
|||||||
[[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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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>
|
||||||
|
|
||||||
@@ -795,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)]
|
||||||
@@ -888,6 +901,18 @@ import "github.com/duke-git/lancet/v2/maputil"
|
|||||||
- **<big>HasKey</big>** : checks if map has key or not.
|
- **<big>HasKey</big>** : checks if map has key or not.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#HasKey)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#HasKey)]
|
||||||
[[play](https://go.dev/play/p/isZZHOsDhFc)]
|
[[play](https://go.dev/play/p/isZZHOsDhFc)]
|
||||||
|
- **<big>GetOrSet</big>** : returns value of the given key or set the given value value if not present.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#GetOrSet)]
|
||||||
|
[[play](https://go.dev/play/p/IVQwO1OkEJC)]
|
||||||
|
- **<big>MapToStruct</big>** : converts map to struct.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapToStruct)]
|
||||||
|
[[play](https://go.dev/play/p/7wYyVfX38Dp)]
|
||||||
|
- **<big>ToSortedSlicesDefault</big>** : converts a map to two slices sorted by key: one for the keys and another for the values.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesDefault)]
|
||||||
|
[[play](https://go.dev/play/p/43gEM2po-qy)]
|
||||||
|
- **<big>ToSortedSlicesWithComparator</big>** : converts a map to two slices sorted by key and using a custom comparison function: one for the keys and another for the values.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesWithComparator)]
|
||||||
|
[[play](https://go.dev/play/p/0nlPo6YLdt3)]
|
||||||
- **<big>NewConcurrentMap</big>** : creates a ConcurrentMap with specific shard count.
|
- **<big>NewConcurrentMap</big>** : creates a ConcurrentMap with specific shard count.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewConcurrentMap)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewConcurrentMap)]
|
||||||
[[play](https://go.dev/play/p/3PenTPETJT0)]
|
[[play](https://go.dev/play/p/3PenTPETJT0)]
|
||||||
@@ -959,12 +984,16 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
|||||||
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
||||||
- **<big>CeilToFloat</big>** : round float up n decimal places.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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.
|
- **<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)]
|
[[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)]
|
||||||
@@ -1006,6 +1035,7 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
|||||||
[[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.
|
- **<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)]
|
[[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>
|
||||||
@@ -1098,7 +1128,7 @@ import "github.com/duke-git/lancet/v2/pointer"
|
|||||||
- **<big>Unwrap</big>** : return the value from the pointer.
|
- **<big>Unwrap</big>** : return the value from the pointer.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#Unwrap)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#Unwrap)]
|
||||||
[[play](https://go.dev/play/p/cgeu3g7cjWb)]
|
[[play](https://go.dev/play/p/cgeu3g7cjWb)]
|
||||||
- **<big>UnwarpOr</big>** : UnwarpOr returns the value from the pointer or fallback if the pointer is nil.
|
- **<big>UnwrapOr</big>** : UnwrapOr returns the value from the pointer or fallback if the pointer is nil.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#UnwrapOr)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/pointer.md#UnwrapOr)]
|
||||||
[[play](https://go.dev/play/p/mmNaLC38W8C)]
|
[[play](https://go.dev/play/p/mmNaLC38W8C)]
|
||||||
- **<big>UnwarpOrDefault</big>** : UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
|
- **<big>UnwarpOrDefault</big>** : UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
|
||||||
@@ -1137,7 +1167,7 @@ import "github.com/duke-git/lancet/v2/random"
|
|||||||
- **<big>UUIdV4</big>** : generate a random UUID of version 4 according to RFC 4122.
|
- **<big>UUIdV4</big>** : generate a random UUID of version 4 according to RFC 4122.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#UUIdV4)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#UUIdV4)]
|
||||||
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
|
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
|
||||||
- **<big>RandUniqueIntSlice</big>** : generate a slice of random int of length n that do not repeat.
|
- **<big>RandUniqueIntSlice</big>** : generate a slice of random int that do not repeat.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandUniqueIntSlice)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandUniqueIntSlice)]
|
||||||
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
|
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
|
||||||
- **<big>RandSymbolChar</big>** : Generate a random symbol char of specified length.
|
- **<big>RandSymbolChar</big>** : Generate a random symbol char of specified length.
|
||||||
@@ -1148,7 +1178,7 @@ import "github.com/duke-git/lancet/v2/random"
|
|||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloat)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloat)]
|
||||||
[[play](https://go.dev/play/p/zbD_tuobJtr)]
|
[[play](https://go.dev/play/p/zbD_tuobJtr)]
|
||||||
|
|
||||||
- **<big>RandFloats</big>** : Generate a slice of random float64 numbers of length n that do not repeat.
|
- **<big>RandFloats</big>** : Generate a slice of random float64 numbers that do not repeat.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloats)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloats)]
|
||||||
[[play](https://go.dev/play/p/I3yndUQ-rhh)]
|
[[play](https://go.dev/play/p/I3yndUQ-rhh)]
|
||||||
|
|
||||||
@@ -1180,11 +1210,13 @@ import "github.com/duke-git/lancet/v2/retry"
|
|||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#BackoffStrategy)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#BackoffStrategy)]
|
||||||
- **<big>RetryWithCustomBackoff</big>** : set abitary custom backoff strategy.
|
- **<big>RetryWithCustomBackoff</big>** : set abitary custom backoff strategy.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithCustomBackoff)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithCustomBackoff)]
|
||||||
|
[[play](https://go.dev/play/p/jIm_o2vb5Y4)]
|
||||||
- **<big>RetryWithLinearBackoff</big>** : set linear strategy backoff.
|
- **<big>RetryWithLinearBackoff</big>** : set linear strategy backoff.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithLinearBackoff)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithLinearBackoff)]
|
||||||
|
[[play](https://go.dev/play/p/PDet2ZQZwcB)]
|
||||||
- **<big>RetryWithExponentialWithJitterBackoff</big>** : set exponential strategy backoff.
|
- **<big>RetryWithExponentialWithJitterBackoff</big>** : set exponential strategy backoff.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
|
||||||
|
[[play](https://go.dev/play/p/xp1avQmn16X)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. <a href="#index">index</a></h3>
|
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. <a href="#index">index</a></h3>
|
||||||
@@ -1384,9 +1416,16 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
- **<big>Unique</big>** : remove duplicate elements in slice.
|
- **<big>Unique</big>** : remove duplicate elements in slice.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Unique)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Unique)]
|
||||||
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
|
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
|
||||||
- **<big>UniqueBy</big>** : call iteratee func with every item of slice, then remove duplicated.
|
- **<big>UniqueBy</big>** : remove duplicate elements from the input slice based on the values returned by the iteratee function.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueBy)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueBy)]
|
||||||
[[play](https://go.dev/play/p/UR323iZLDpv)]
|
[[play](https://go.dev/play/p/UR323iZLDpv)]
|
||||||
|
- **<big>UniqueByComparator</big>** : remove duplicate elements from the input slice using the provided comparator function.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByComparator)]
|
||||||
|
- **<big>UniqueByField</big>** : remove duplicate elements in struct slice by struct field.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByField)]
|
||||||
|
[[play](https://go.dev/play/p/6cifcZSPIGu)]
|
||||||
|
- **<big>UniqueByParallel</big>** : remove duplicate elements from the slice by parallel.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#UniqueByParallel)]
|
||||||
- **<big>Union</big>** : creates a slice of unique elements, in order, from all given slices.
|
- **<big>Union</big>** : creates a slice of unique elements, in order, from all given slices.
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Union)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Union)]
|
||||||
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
|
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
|
||||||
@@ -1413,6 +1452,15 @@ 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)]
|
||||||
|
[[play](https://go.dev/play/p/0_2rlLEMBXL)]
|
||||||
|
- **<big>LeftPadding</big>** : adds padding to the left begin of a slice.
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#LeftPadding)]
|
||||||
|
[[play](https://go.dev/play/p/jlQVoelLl2k)]
|
||||||
|
|
||||||
|
|
||||||
<h3 id="stream"> 19. Stream package implements a sequence of elements supporting sequential and operations. this package is an experiment to explore if stream in go can work as the way java does. its function is very limited. <a href="#index">index</a></h3>
|
<h3 id="stream"> 19. Stream package implements a sequence of elements supporting sequential and operations. this package is an experiment to explore if stream in go can work as the way java does. its function is very limited. <a href="#index">index</a></h3>
|
||||||
@@ -1660,8 +1708,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>
|
||||||
|
|
||||||
@@ -2001,11 +2052,15 @@ import "github.com/duke-git/lancet/v2/xerror"
|
|||||||
|
|
||||||
## How to Contribute
|
## How to Contribute
|
||||||
|
|
||||||
#### [Contributing Guide](./CONTRIBUTING.en-US.md)
|
#### [Contribution Guide](./CONTRIBUTION.md)
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
Thank you to all the people who contributed to lancet!
|
Thank you to all the people who contributed to lancet!
|
||||||
|
|
||||||
<a href="https://github.com/duke-git/lancet/graphs/contributors">
|
<a href="https://github.com/duke-git/lancet/graphs/contributors">
|
||||||
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
|
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
|
[](https://star-history.com/#duke-git/lancet&Date)
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -659,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)]
|
||||||
@@ -730,8 +733,10 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
|||||||
[[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>** : 从文件的指定偏移读取块并返回块内所有行。
|
- **<big>ChunkRead</big>** : 从文件的指定偏移读取块并返回块内所有行。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ChunkRead)]
|
[[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>** : 并行读取文件并将每个块的行发送到指定通道。
|
- **<big>ParallelChunkRead</big>** : 并行读取文件并将每个块的行发送到指定通道。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ParallelChunkRead)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ParallelChunkRead)]
|
||||||
|
[[play](https://go.dev/play/p/teMXnCsdSEw)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -799,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
|
||||||
@@ -889,6 +903,18 @@ 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>GetOrSet</big>** : 返回给定键的值,如果不存在则设置该值。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#GetOrSet)]
|
||||||
|
[[play](https://go.dev/play/p/IVQwO1OkEJC)]
|
||||||
|
- **<big>MapToStruct</big>** : 将map转成struct。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#MapToStruct)]
|
||||||
|
[[play](https://go.dev/play/p/7wYyVfX38Dp)]
|
||||||
|
- **<big>ToSortedSlicesDefault</big>** : 将map的key和value转化成两个根据key的值从小到大排序的切片,value切片中元素的位置与key对应。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesDefault)]
|
||||||
|
[[play](https://go.dev/play/p/43gEM2po-qy)]
|
||||||
|
- **<big>ToSortedSlicesWithComparator</big>** : 将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片,value切片中元素的位置与key对应。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesWithComparator)]
|
||||||
|
[[play](https://go.dev/play/p/0nlPo6YLdt3)]
|
||||||
- **<big>NewConcurrentMap</big>** : ConcurrentMap 协程安全的 map 结构。
|
- **<big>NewConcurrentMap</big>** : ConcurrentMap 协程安全的 map 结构。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewConcurrentMap)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewConcurrentMap)]
|
||||||
[[play](https://go.dev/play/p/3PenTPETJT0)]
|
[[play](https://go.dev/play/p/3PenTPETJT0)]
|
||||||
@@ -960,12 +986,16 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
|||||||
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
||||||
- **<big>CeilToFloat</big>** : 向上舍入(进一法),保留n位小数。
|
- **<big>CeilToFloat</big>** : 向上舍入(进一法),保留n位小数。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToFloat)]
|
[[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位小数,返回字符串。
|
- **<big>CeilToString</big>** : 向上舍入(进一法),保留n位小数,返回字符串。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToString)]
|
[[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位小数。
|
- **<big>FloorToFloat</big>** : 向下舍入(去尾法),保留n位小数。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToFloat)]
|
[[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位小数,返回字符串。
|
- **<big>FloorToString</big>** : 向下舍入(去尾法),保留n位小数,返回字符串。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToString)]
|
[[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)]
|
||||||
@@ -1007,7 +1037,7 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
|||||||
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
|
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
|
||||||
- **<big>Div</big>** : 除法运算。
|
- **<big>Div</big>** : 除法运算。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Div)]
|
[[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>
|
||||||
@@ -1139,7 +1169,7 @@ import "github.com/duke-git/lancet/v2/random"
|
|||||||
- **<big>UUIdV4</big>** : 生成 UUID v4 字符串。
|
- **<big>UUIdV4</big>** : 生成 UUID v4 字符串。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#UUIdV4)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#UUIdV4)]
|
||||||
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
|
[[play](https://go.dev/play/p/_Z9SFmr28ft)]
|
||||||
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的长度为 n 的随机 int 切片。
|
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的随机int切片。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandUniqueIntSlice)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandUniqueIntSlice)]
|
||||||
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
|
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
|
||||||
- **<big>RandSymbolChar</big>** : 生成给定长度的随机符号字符串。
|
- **<big>RandSymbolChar</big>** : 生成给定长度的随机符号字符串。
|
||||||
@@ -1180,10 +1210,13 @@ import "github.com/duke-git/lancet/v2/retry"
|
|||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#BackoffStrategy)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#BackoffStrategy)]
|
||||||
- **<big>RetryWithCustomBackoff</big>** : 设置自定义退避策略。
|
- **<big>RetryWithCustomBackoff</big>** : 设置自定义退避策略。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithCustomBackoff)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithCustomBackoff)]
|
||||||
|
[[play](https://go.dev/play/p/jIm_o2vb5Y4)]
|
||||||
- **<big>RetryWithLinearBackoff</big>** : 设置线性策略退避。
|
- **<big>RetryWithLinearBackoff</big>** : 设置线性策略退避。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithLinearBackoff)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithLinearBackoff)]
|
||||||
|
[[play](https://go.dev/play/p/PDet2ZQZwcB)]
|
||||||
- **<big>RetryWithExponentialWithJitterBackoff</big>** : 设置指数策略退避。
|
- **<big>RetryWithExponentialWithJitterBackoff</big>** : 设置指数策略退避。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
|
||||||
|
[[play](https://go.dev/play/p/xp1avQmn16X)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1384,9 +1417,16 @@ import "github.com/duke-git/lancet/v2/slice"
|
|||||||
- **<big>Unique</big>** : 删除切片中的重复元素。
|
- **<big>Unique</big>** : 删除切片中的重复元素。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Unique)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Unique)]
|
||||||
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
|
[[play](https://go.dev/play/p/AXw0R3ZTE6a)]
|
||||||
- **<big>UniqueBy</big>** : 对切片的每个元素调用 iteratee 函数,然后删除重复元素。
|
- **<big>UniqueBy</big>** : 根据迭代函数返回的值,从输入切片中移除重复元素。此函数保持元素的顺序。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueBy)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueBy)]
|
||||||
[[play](https://go.dev/play/p/UR323iZLDpv)]
|
[[play](https://go.dev/play/p/UR323iZLDpv)]
|
||||||
|
- **<big>UniqueByComparator</big>** : 使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByComparator)]
|
||||||
|
- **<big>UniqueByField</big>** : 根据struct字段对struct切片去重复。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByField)]
|
||||||
|
[[play](https://go.dev/play/p/6cifcZSPIGu)]
|
||||||
|
- **<big>UniqueByParallel</big>** : 并发的从输入切片中移除重复元素,结果保持元素的顺序。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#UniqueByParallel)]
|
||||||
- **<big>Union</big>** : 合并多个切片。
|
- **<big>Union</big>** : 合并多个切片。
|
||||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Union)]
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Union)]
|
||||||
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
|
[[play](https://go.dev/play/p/hfXV1iRIZOf)]
|
||||||
@@ -1412,6 +1452,15 @@ 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)]
|
||||||
|
[[play](https://go.dev/play/p/0_2rlLEMBXL)]
|
||||||
|
- **<big>LeftPadding</big>** : 在切片的左部添加元素。
|
||||||
|
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#LeftPadding)]
|
||||||
|
[[play](https://go.dev/play/p/jlQVoelLl2k)]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1502,6 +1551,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
|
||||||
@@ -1663,8 +1713,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>
|
||||||
|
|
||||||
@@ -2004,7 +2057,7 @@ import "github.com/duke-git/lancet/v2/xerror"
|
|||||||
|
|
||||||
## 如何贡献代码
|
## 如何贡献代码
|
||||||
|
|
||||||
#### [贡献代码指南](./CONTRIBUTING.zh-CN.md)
|
#### [代码贡献指南](./CONTRIBUTION.zh-CN.md)
|
||||||
|
|
||||||
## 贡献者
|
## 贡献者
|
||||||
|
|
||||||
@@ -2013,3 +2066,7 @@ import "github.com/duke-git/lancet/v2/xerror"
|
|||||||
<a href="https://github.com/duke-git/lancet/graphs/contributors">
|
<a href="https://github.com/duke-git/lancet/graphs/contributors">
|
||||||
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
|
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
## GitHub Stars
|
||||||
|
|
||||||
|
[](https://star-history.com/#duke-git/lancet&Date)
|
||||||
@@ -49,9 +49,7 @@ func Or[T, U any](a T, b U) bool {
|
|||||||
// Xor returns true if a or b but not both is truthy.
|
// Xor returns true if a or b but not both is truthy.
|
||||||
// Play: https://go.dev/play/p/gObZrW7ZbG8
|
// Play: https://go.dev/play/p/gObZrW7ZbG8
|
||||||
func Xor[T, U any](a T, b U) bool {
|
func Xor[T, U any](a T, b U) bool {
|
||||||
valA := Bool(a)
|
return Bool(a) != Bool(b)
|
||||||
valB := Bool(b)
|
|
||||||
return (valA || valB) && valA != valB
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nor returns true if neither a nor b is truthy.
|
// Nor returns true if neither a nor b is truthy.
|
||||||
@@ -63,9 +61,7 @@ func Nor[T, U any](a T, b U) bool {
|
|||||||
// Xnor returns true if both a and b or neither a nor b are truthy.
|
// Xnor returns true if both a and b or neither a nor b are truthy.
|
||||||
// Play: https://go.dev/play/p/OuDB9g51643
|
// Play: https://go.dev/play/p/OuDB9g51643
|
||||||
func Xnor[T, U any](a T, b U) bool {
|
func Xnor[T, U any](a T, b U) bool {
|
||||||
valA := Bool(a)
|
return Bool(a) == Bool(b)
|
||||||
valB := Bool(b)
|
|
||||||
return (valA && valB) || (!valA && !valB)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nand returns false if both a and b are truthy.
|
// Nand returns false if both a and b are truthy.
|
||||||
|
|||||||
@@ -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 ""
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export const slugify = (str: string): string =>
|
|||||||
export const commonConfig = defineConfig({
|
export const commonConfig = defineConfig({
|
||||||
title: 'Lancet',
|
title: 'Lancet',
|
||||||
appearance: true,
|
appearance: true,
|
||||||
|
ignoreDeadLinks: true,
|
||||||
|
|
||||||
markdown: {
|
markdown: {
|
||||||
theme: {
|
theme: {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -1064,7 +1064,7 @@ func main() {
|
|||||||
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
|
||||||
@@ -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
|
||||||
@@ -1109,7 +1109,7 @@ 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")
|
||||||
@@ -1132,11 +1132,11 @@ func main() {
|
|||||||
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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
|
|
||||||
- [New](#New)
|
- [New](#New)
|
||||||
- [FromSlice](#FromSlice)
|
- [FromSlice](#FromSlice)
|
||||||
- [Values](#Values)
|
- [Values<sup>deprecated</sup>](#Values)
|
||||||
- [Add](#Add)
|
- [Add](#Add)
|
||||||
- [AddIfNotExist](#AddIfNotExist)
|
- [AddIfNotExist](#AddIfNotExist)
|
||||||
- [AddIfNotExistBy](#AddIfNotExistBy)
|
- [AddIfNotExistBy](#AddIfNotExistBy)
|
||||||
@@ -101,10 +101,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="Values">Values<sup>deprecated</sup></span>
|
### <span id="Values">Values</span>
|
||||||
|
|
||||||
<p>获取集合中所有元素的切片<br>
|
<p>获取集合中所有元素的切片。</p>
|
||||||
<a href='#ToSlice'>ToSlice()</a> 方法提供与 Values 方法相同的功能</p>
|
|
||||||
|
> ⚠️ 本函数已弃用,使用`ToSlice`代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
|||||||
@@ -976,7 +976,7 @@ func main() {
|
|||||||
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
|
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/r0hPmKWhsgf)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -1035,7 +1035,7 @@ func main() {
|
|||||||
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
|
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/teMXnCsdSEw)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ import (
|
|||||||
- [Before](#Before)
|
- [Before](#Before)
|
||||||
- [CurryFn](#CurryFn)
|
- [CurryFn](#CurryFn)
|
||||||
- [Compose](#Compose)
|
- [Compose](#Compose)
|
||||||
- [Debounced](#Debounced)
|
- [Debounce](#Debounce)
|
||||||
|
- [Debounced<sup>deprecated</sup>](#Debounced)
|
||||||
- [Delay](#Delay)
|
- [Delay](#Delay)
|
||||||
- [Schedule](#Schedule)
|
- [Schedule](#Schedule)
|
||||||
- [Pipeline](#Pipeline)
|
- [Pipeline](#Pipeline)
|
||||||
@@ -40,6 +41,7 @@ import (
|
|||||||
- [Xnor](#Xnor)
|
- [Xnor](#Xnor)
|
||||||
- [Nand](#Nand)
|
- [Nand](#Nand)
|
||||||
- [AcceptIf](#AcceptIf)
|
- [AcceptIf](#AcceptIf)
|
||||||
|
- [Throttle](#Throttle)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
@@ -193,9 +195,58 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="Debounce">Debounce</span>
|
||||||
|
|
||||||
|
<p>创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
callCount := 0
|
||||||
|
fn := func() {
|
||||||
|
callCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
debouncedFn()
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
fmt.Println(callCount)
|
||||||
|
|
||||||
|
debouncedFn()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
fmt.Println(callCount)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 1
|
||||||
|
// 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="Debounced">Debounced</span>
|
### <span id="Debounced">Debounced</span>
|
||||||
|
|
||||||
<p>创建一个debounced函数,该函数延迟调用fn直到自上次调用debounced函数后等待持续时间过去。</p>
|
<p>创建一个函数的去抖动版本。</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用. 使用 `Debounce` 代替.
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -424,7 +475,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 +512,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 +547,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 +587,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 +629,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 +666,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 +703,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 +714,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 {
|
||||||
@@ -691,4 +741,46 @@ func main() {
|
|||||||
// false
|
// false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Throttle">Throttle</span>
|
||||||
|
|
||||||
|
<p>创建一个函数的节流版本。返回的函数保证在每个时间间隔内最多只会被调用一次。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Throttle(fn func(), interval time.Duration) func()
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
fn := func() {
|
||||||
|
callCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
throttledFn := function.Throttle(fn, 1*time.Second)
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
throttledFn()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
fmt.Println(callCount)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 1
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -44,6 +44,9 @@ import (
|
|||||||
- [Minus](#Minus)
|
- [Minus](#Minus)
|
||||||
- [IsDisjoint](#IsDisjoint)
|
- [IsDisjoint](#IsDisjoint)
|
||||||
- [HasKey](#HasKey)
|
- [HasKey](#HasKey)
|
||||||
|
- [MapToStruct](#MapToStruct)
|
||||||
|
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
|
||||||
|
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
|
||||||
- [NewConcurrentMap](#NewConcurrentMap)
|
- [NewConcurrentMap](#NewConcurrentMap)
|
||||||
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
||||||
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
||||||
@@ -52,6 +55,9 @@ import (
|
|||||||
- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete)
|
- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete)
|
||||||
- [ConcurrentMap_Has](#ConcurrentMap_Has)
|
- [ConcurrentMap_Has](#ConcurrentMap_Has)
|
||||||
- [ConcurrentMap_Range](#ConcurrentMap_Range)
|
- [ConcurrentMap_Range](#ConcurrentMap_Range)
|
||||||
|
- [GetOrSet](#GetOrSet)
|
||||||
|
- [SortByKey](#SortByKey)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -946,14 +952,6 @@ func main() {
|
|||||||
|
|
||||||
<p>检查map是否包含某个key。用于代替以下样板代码:</p>
|
<p>检查map是否包含某个key。用于代替以下样板代码:</p>
|
||||||
|
|
||||||
```go
|
|
||||||
_, haskey := amap["baz"];
|
|
||||||
|
|
||||||
if haskey {
|
|
||||||
fmt.Println("map has key baz")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -988,6 +986,141 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="MapToStruct">MapToStruct</span>
|
||||||
|
|
||||||
|
<p>将map转成struct。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func MapToStruct(m map[string]any, structObj any) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7wYyVfX38Dp)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
personReqMap := map[string]any{
|
||||||
|
"name": "Nothin",
|
||||||
|
"max_age": 35,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersonReq struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MaxAge int `json:"max_age"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
|
}
|
||||||
|
var personReq PersonReq
|
||||||
|
_ = maputil.MapToStruct(personReqMap, &personReq)
|
||||||
|
fmt.Println(personReq)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {Nothin 35 1 10}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToSortedSlicesDefault">ToSortedSlicesDefault</span>
|
||||||
|
|
||||||
|
<p>将map的key和value转化成两个根据key的值从小到大排序的切片,value切片中元素的位置与key对应。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/43gEM2po-qy)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, values := ToSortedSlicesDefault(m)
|
||||||
|
|
||||||
|
fmt.Println(keys)
|
||||||
|
fmt.Println(values)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2 3]
|
||||||
|
// [a b c]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToSortedSlicesWithComparator">ToSortedSlicesWithComparator</span>
|
||||||
|
|
||||||
|
<p>将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片,value切片中元素的位置与key对应。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/0nlPo6YLdt3)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m1 := map[time.Time]string{
|
||||||
|
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
|
||||||
|
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
|
||||||
|
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys1, values1 := maputil.ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
|
||||||
|
return a.Before(b)
|
||||||
|
})
|
||||||
|
|
||||||
|
m2 := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
keys2, values2 := maputil.ToSortedSlicesWithComparator(m2, func(a, b int) bool {
|
||||||
|
return a > b
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(keys2)
|
||||||
|
fmt.Println(values2)
|
||||||
|
|
||||||
|
fmt.Println(keys1)
|
||||||
|
fmt.Println(values1)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [3 2 1]
|
||||||
|
// [c b a]
|
||||||
|
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
|
||||||
|
// [yesterday today tomorrow]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="NewConcurrentMap">NewConcurrentMap</span>
|
### <span id="NewConcurrentMap">NewConcurrentMap</span>
|
||||||
|
|
||||||
<p>ConcurrentMap协程安全的map结构。</p>
|
<p>ConcurrentMap协程安全的map结构。</p>
|
||||||
@@ -1050,15 +1183,15 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
||||||
fmt.Println(val, ok)
|
fmt.Println(val, ok)
|
||||||
wg2.Done()
|
wg2.Done()
|
||||||
}(j)
|
}(j)
|
||||||
}
|
}
|
||||||
wg2.Wait()
|
wg2.Wait()
|
||||||
|
|
||||||
// output: (order may change)
|
// output: (order may change)
|
||||||
// 1 true
|
// 1 true
|
||||||
@@ -1104,15 +1237,15 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
||||||
fmt.Println(val, ok)
|
fmt.Println(val, ok)
|
||||||
wg2.Done()
|
wg2.Done()
|
||||||
}(j)
|
}(j)
|
||||||
}
|
}
|
||||||
wg2.Wait()
|
wg2.Wait()
|
||||||
|
|
||||||
// output: (order may change)
|
// output: (order may change)
|
||||||
// 1 true
|
// 1 true
|
||||||
@@ -1202,7 +1335,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
cm.Delete(fmt.Sprintf("%d", n))
|
cm.Delete(fmt.Sprintf("%d", n))
|
||||||
@@ -1248,7 +1381,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n))
|
val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n))
|
||||||
@@ -1298,7 +1431,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
|
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
@@ -1353,3 +1486,78 @@ func main() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="GetOrSet">GetOrSet</span>
|
||||||
|
|
||||||
|
<p>返回给定键的值,如果不存在则设置该值。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/IVQwO1OkEJC)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
}
|
||||||
|
|
||||||
|
result1 := maputil.GetOrSet(m, 1, "1")
|
||||||
|
result2 := maputil.GetOrSet(m, 2, "b")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// a
|
||||||
|
// b
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="SortByKey">SortByKey</span>
|
||||||
|
|
||||||
|
<p>对传入的map根据key进行排序,返回排序后的map。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := map[int]string{
|
||||||
|
3: "c",
|
||||||
|
1: "a",
|
||||||
|
4: "d",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
|
||||||
|
result := maputil.SortByKey(m)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// map[1:a 2:b 3:c 4:d]
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -508,7 +508,7 @@ func main() {
|
|||||||
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/8hOeSADZPCo)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -544,7 +544,7 @@ func main() {
|
|||||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wy5bYEyUKKG)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -577,10 +577,10 @@ func main() {
|
|||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vbCBrQHZEED)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -613,10 +613,10 @@ func main() {
|
|||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Qk9KPd2IdDb)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -1130,7 +1130,7 @@ func main() {
|
|||||||
|
|
||||||
### <span id="Div">Div</span>
|
### <span id="Div">Div</span>
|
||||||
|
|
||||||
<p>除法运算.</p>
|
<p>除法运算。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -1138,7 +1138,7 @@ func main() {
|
|||||||
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
|
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/WLxDdGXXYat)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -1156,6 +1156,7 @@ func main() {
|
|||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
fmt.Println(result3)
|
fmt.Println(result3)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// 2.25
|
// 2.25
|
||||||
// 0.5
|
// 0.5
|
||||||
|
|||||||
@@ -624,7 +624,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="HttpGet">HttpGet</span>
|
### <span id="HttpGet">HttpGet</span>
|
||||||
|
|
||||||
<p>发送http get请求。(已废弃:使用SendRequest)</p>
|
<p>发送http get请求。</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -666,7 +668,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="HttpPost">HttpPost</span>
|
### <span id="HttpPost">HttpPost</span>
|
||||||
|
|
||||||
<p>发送http post请求。(已废弃:使用SendRequest)</p>
|
<p>发送http post请求。</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -713,7 +717,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="HttpPut">HttpPut</span>
|
### <span id="HttpPut">HttpPut</span>
|
||||||
|
|
||||||
<p>发送http put请求。(已废弃:使用SendRequest)</p>
|
<p>发送http put请求。</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -763,7 +769,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="HttpDelete">HttpDelete</span>
|
### <span id="HttpDelete">HttpDelete</span>
|
||||||
|
|
||||||
<p>发送http delete请求。(已废弃:使用SendRequest)</p>
|
<p>发送http delete请求。</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -802,7 +810,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="HttpPatch">HttpPatch</span>
|
### <span id="HttpPatch">HttpPatch</span>
|
||||||
|
|
||||||
<p>发送http patch请求。(已废弃:使用SendRequest)</p>
|
<p>发送http patch请求。</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用`SendRequest`代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import (
|
|||||||
- [Of](#Of)
|
- [Of](#Of)
|
||||||
- [Unwrap](#Unwrap)
|
- [Unwrap](#Unwrap)
|
||||||
- [ExtractPointer](#ExtractPointer)
|
- [ExtractPointer](#ExtractPointer)
|
||||||
- [UnwarpOr](#UnwarpOr)
|
- [UnwrapOr](#UnwrapOr)
|
||||||
- [UnwarpOrDefault](#UnwarpOrDefault)
|
- [UnwrapOrDefault](#UnwrapOrDefault)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -136,14 +136,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="UnwarpOr">UnwarpOr</span>
|
### <span id="UnwrapOr">UnwrapOr</span>
|
||||||
|
|
||||||
<p>返回指针的值,如果指针为零值,则返回fallback。</p>
|
<p>返回指针的值,如果指针为零值,则返回fallback。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
UnwarpOr[T any](p *T, fallback T) T
|
UnwrapOr[T any](p *T, fallback T) T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/mmNaLC38W8C)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/mmNaLC38W8C)</span></b>
|
||||||
@@ -163,10 +163,10 @@ func main() {
|
|||||||
var c *int
|
var c *int
|
||||||
var d *string
|
var d *string
|
||||||
|
|
||||||
result1 := pointer.UnwarpOr(&a, 456)
|
result1 := pointer.UnwrapOr(&a, 456)
|
||||||
result2 := pointer.UnwarpOr(&b, "abc")
|
result2 := pointer.UnwrapOr(&b, "abc")
|
||||||
result3 := pointer.UnwarpOr(c, 456)
|
result3 := pointer.UnwrapOr(c, 456)
|
||||||
result4 := pointer.UnwarpOr(d, "def")
|
result4 := pointer.UnwrapOr(d, "def")
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
@@ -181,14 +181,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="UnwarpOrDefault">UnwarpOrDefault</span>
|
### <span id="UnwrapOrDefault">UnwrapOrDefault</span>
|
||||||
|
|
||||||
<p>返回指针的值,如果指针为零值,则返回相应零值。</p>
|
<p>返回指针的值,如果指针为零值,则返回相应零值。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
UnwarpOrDefault[T any](p *T) T
|
UnwrapOrDefault[T any](p *T) T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
|
||||||
@@ -208,10 +208,10 @@ func main() {
|
|||||||
var c *int
|
var c *int
|
||||||
var d *string
|
var d *string
|
||||||
|
|
||||||
result1 := pointer.UnwarpOrDefault(&a)
|
result1 := pointer.UnwrapOrDefault(&a)
|
||||||
result2 := pointer.UnwarpOrDefault(&b)
|
result2 := pointer.UnwrapOrDefault(&b)
|
||||||
result3 := pointer.UnwarpOrDefault(c)
|
result3 := pointer.UnwrapOrDefault(c)
|
||||||
result4 := pointer.UnwarpOrDefault(d)
|
result4 := pointer.UnwrapOrDefault(d)
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
|
|||||||
@@ -25,15 +25,22 @@ import (
|
|||||||
- [RandBytes](#RandBytes)
|
- [RandBytes](#RandBytes)
|
||||||
- [RandInt](#RandInt)
|
- [RandInt](#RandInt)
|
||||||
- [RandString](#RandString)
|
- [RandString](#RandString)
|
||||||
|
- [RandFromGivenSlice](#RandFromGivenSlice)
|
||||||
|
- [RandSliceFromGivenSlice](#RandSliceFromGivenSlice)
|
||||||
- [RandUpper](#RandUpper)
|
- [RandUpper](#RandUpper)
|
||||||
- [RandLower](#RandLower)
|
- [RandLower](#RandLower)
|
||||||
- [RandNumeral](#RandNumeral)
|
- [RandNumeral](#RandNumeral)
|
||||||
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
||||||
- [RandSymbolChar](#RandSymbolChar)
|
- [RandSymbolChar](#RandSymbolChar)
|
||||||
- [UUIdV4](#UUIdV4)
|
- [UUIdV4](#UUIdV4)
|
||||||
|
- [RandIntSlice](#RandIntSlice)
|
||||||
- [RandUniqueIntSlice](#RandUniqueIntSlice)
|
- [RandUniqueIntSlice](#RandUniqueIntSlice)
|
||||||
- [RandFloat](#RandFloat)
|
- [RandFloat](#RandFloat)
|
||||||
- [RandFloats](#RandFloats)
|
- [RandFloats](#RandFloats)
|
||||||
|
- [RandStringSlice](#RandStringSlice)
|
||||||
|
- [RandBool](#RandBool)
|
||||||
|
- [RandBoolSlice](#RandBoolSlice)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -117,6 +124,60 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="RandFromGivenSlice">RandFromGivenSlice</span>
|
||||||
|
|
||||||
|
<p>从给定切片中随机生成元素</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandFromGivenSlice[T any](slice []T) T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nicknames := []string{"张三", "李四", "王五", "赵六", "钱七"}
|
||||||
|
randElm := random.RandFromGivenSlice(nicknames)
|
||||||
|
fmt.Println(randElm)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RandSliceFromGivenSlice">RandSliceFromGivenSlice</span>
|
||||||
|
|
||||||
|
<p>从给定切片中生成长度为 num 的随机切片</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon","mango", "nectarine", "orange"}
|
||||||
|
chosen3goods := random.RandSliceFromGivenSlice(goods, 3, false)
|
||||||
|
fmt.Println(chosen3goods)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="RandUpper">RandUpper</span>
|
### <span id="RandUpper">RandUpper</span>
|
||||||
|
|
||||||
<p>生成给定长度的随机大写字母字符串</p>
|
<p>生成给定长度的随机大写字母字符串</p>
|
||||||
@@ -276,14 +337,40 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
|
### <span id="RandIntSlice">RandIntSlice</span>
|
||||||
|
|
||||||
<p>生成一个不重复的长度为n的随机int切片。</p>
|
<p>生成一个特定长度的随机int切片,数值范围[min, max)。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func RandUniqueIntSlice(n, min, max int) []int
|
func RandIntSlice(length, min, max int) []int
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result := random.RandIntSlice(5, 0, 10)
|
||||||
|
fmt.Println(result) //[1 2 7 1 5] (random)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
|
||||||
|
|
||||||
|
<p>生成一个特定长度的,数值不重复的随机int切片,数值范围[min, max)。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandUniqueIntSlice(length, min, max int) []int
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uBkRSOz73Ec)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uBkRSOz73Ec)</span></b>
|
||||||
@@ -304,7 +391,7 @@ func main() {
|
|||||||
|
|
||||||
### <span id="RandFloat">RandFloat</span>
|
### <span id="RandFloat">RandFloat</span>
|
||||||
|
|
||||||
<p>生成随机float64数字,可以指定范围和精度。</p>
|
<p>生成一个随机float64数值,可以指定精度。数值范围[min, max)。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -330,7 +417,7 @@ func main() {
|
|||||||
|
|
||||||
### <span id="RandFloats">RandFloats</span>
|
### <span id="RandFloats">RandFloats</span>
|
||||||
|
|
||||||
<p>生成随机float64数字切片,指定长度,范围和精度.</p>
|
<p>生成一个特定长度的随机float64切片,可以指定数值精度。数值范围[min, max)。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -352,4 +439,85 @@ func main() {
|
|||||||
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
|
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
|
||||||
fmt.Println(floatNumber) //[3.42 3.99 1.3 2.38 4.23] (random)
|
fmt.Println(floatNumber) //[3.42 3.99 1.3 2.38 4.23] (random)
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RandStringSlice">RandStringSlice</span>
|
||||||
|
|
||||||
|
<p>生成随机字符串slice. 字符串类型需要是以下几种或者它们的组合: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandStringSlice(charset string, sliceLen, strLen int) []string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>实例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
strs := random.RandStringSlice(random.Letters, 4, 6)
|
||||||
|
fmt.Println(strs)
|
||||||
|
|
||||||
|
// output random string slice like below:
|
||||||
|
//[CooSMq RUFjDz FAeMPf heRyGv]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RandBool">RandBool</span>
|
||||||
|
|
||||||
|
<p>生成随机bool值(true or false)。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandBool() bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>实例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result := random.RandBool()
|
||||||
|
fmt.Println(result) // true or false (random)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RandBoolSlice">RandBoolSlice</span>
|
||||||
|
|
||||||
|
<p>生成特定长度的随机bool slice。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandBoolSlice(length int) []bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>实例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result := random.RandBoolSlice(2)
|
||||||
|
fmt.Println(result) // [true false] (random)
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -331,7 +331,7 @@ func main() {
|
|||||||
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
|
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jIm_o2vb5Y4)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -384,7 +384,7 @@ func main() {
|
|||||||
func RetryWithLinearBackoff(interval time.Duration) Option
|
func RetryWithLinearBackoff(interval time.Duration) Option
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/PDet2ZQZwcB)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -429,7 +429,7 @@ func main() {
|
|||||||
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
|
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:</b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/xp1avQmn16X)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ slice 包包含操作切片的方法集合。
|
|||||||
|
|
||||||
## 源码:
|
## 源码:
|
||||||
|
|
||||||
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
|
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
|
||||||
|
- [https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go](https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ import (
|
|||||||
- [Equal](#Equal)
|
- [Equal](#Equal)
|
||||||
- [EqualWith](#EqualWith)
|
- [EqualWith](#EqualWith)
|
||||||
- [Filter](#Filter)
|
- [Filter](#Filter)
|
||||||
|
- [FilterConcurrent](#FilterConcurrent)
|
||||||
- [Find<sup>deprecated</sup>](#Find)
|
- [Find<sup>deprecated</sup>](#Find)
|
||||||
- [FindBy](#FindBy)
|
- [FindBy](#FindBy)
|
||||||
- [FindLast<sup>deprecated</sup>](#FindLast)
|
- [FindLast<sup>deprecated</sup>](#FindLast)
|
||||||
@@ -51,6 +53,7 @@ import (
|
|||||||
- [Flatten](#Flatten)
|
- [Flatten](#Flatten)
|
||||||
- [FlattenDeep](#FlattenDeep)
|
- [FlattenDeep](#FlattenDeep)
|
||||||
- [ForEach](#ForEach)
|
- [ForEach](#ForEach)
|
||||||
|
- [ForEachConcurrent](#ForEachConcurrent)
|
||||||
- [ForEachWithBreak](#ForEachWithBreak)
|
- [ForEachWithBreak](#ForEachWithBreak)
|
||||||
- [GroupBy](#GroupBy)
|
- [GroupBy](#GroupBy)
|
||||||
- [GroupWith](#GroupWith)
|
- [GroupWith](#GroupWith)
|
||||||
@@ -61,11 +64,13 @@ import (
|
|||||||
- [IndexOf](#IndexOf)
|
- [IndexOf](#IndexOf)
|
||||||
- [LastIndexOf](#LastIndexOf)
|
- [LastIndexOf](#LastIndexOf)
|
||||||
- [Map](#Map)
|
- [Map](#Map)
|
||||||
|
- [MapConcurrent](#MapConcurrent)
|
||||||
- [FilterMap](#FilterMap)
|
- [FilterMap](#FilterMap)
|
||||||
- [FlatMap](#FlatMap)
|
- [FlatMap](#FlatMap)
|
||||||
- [Merge](#Merge)
|
- [Merge](#Merge)
|
||||||
- [Reverse](#Reverse)
|
- [Reverse](#Reverse)
|
||||||
- [Reduce<sup>deprecated</sup>](#Reduce)
|
- [Reduce<sup>deprecated</sup>](#Reduce)
|
||||||
|
- [ReduceConcurrent](#ReduceConcurrent)
|
||||||
- [ReduceBy](#ReduceBy)
|
- [ReduceBy](#ReduceBy)
|
||||||
- [ReduceRight](#ReduceRight)
|
- [ReduceRight](#ReduceRight)
|
||||||
- [Replace](#Replace)
|
- [Replace](#Replace)
|
||||||
@@ -86,6 +91,9 @@ import (
|
|||||||
- [ToSlicePointer](#ToSlicePointer)
|
- [ToSlicePointer](#ToSlicePointer)
|
||||||
- [Unique](#Unique)
|
- [Unique](#Unique)
|
||||||
- [UniqueBy](#UniqueBy)
|
- [UniqueBy](#UniqueBy)
|
||||||
|
- [UniqueByComparator](#UniqueByComparator)
|
||||||
|
- [UniqueByField](#UniqueByField)
|
||||||
|
- [UniqueByConcurrent](#UniqueByConcurrent)
|
||||||
- [Union](#Union)
|
- [Union](#Union)
|
||||||
- [UnionBy](#UnionBy)
|
- [UnionBy](#UnionBy)
|
||||||
- [UpdateAt](#UpdateAt)
|
- [UpdateAt](#UpdateAt)
|
||||||
@@ -94,6 +102,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>
|
||||||
|
|
||||||
@@ -319,12 +330,12 @@ func main() {
|
|||||||
|
|
||||||
### <span id="Concat">Concat</span>
|
### <span id="Concat">Concat</span>
|
||||||
|
|
||||||
<p>合并多个slices到slice中</p>
|
<p>创建一个新的切片,将传入的切片拼接起来返回。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func Concat[T any](slice []T, slices ...[]T) []T
|
func Concat[T any](slices ...[]T) []T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
|
||||||
@@ -893,10 +904,46 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="Find">Find (废弃:使用 FindBy)</span>
|
### <span id="FilterConcurrent">FilterConcurrent</span>
|
||||||
|
|
||||||
|
<p>对slice并发执行filter操作。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
isEven := func(i, num int) bool {
|
||||||
|
return num%2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
result := slice.FilterConcurrent(nums, isEven, 2)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [2 4]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Find">Find</span>
|
||||||
|
|
||||||
<p>遍历slice的元素,返回第一个通过predicate函数真值测试的元素</p>
|
<p>遍历slice的元素,返回第一个通过predicate函数真值测试的元素</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用`FindBy`代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -965,10 +1012,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="FindLast">FindLast(废弃:使用 FindLastBy)</span>
|
### <span id="FindLast">FindLast</span>
|
||||||
|
|
||||||
<p>遍历slice的元素,返回最后一个通过predicate函数真值测试的元素。</p>
|
<p>遍历slice的元素,返回最后一个通过predicate函数真值测试的元素。</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用`FindLastBy`代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -1132,6 +1181,43 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="ForEachConcurrent">ForEachConcurrent</span>
|
||||||
|
|
||||||
|
<p>对slice并发执行foreach操作。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||||
|
|
||||||
|
result := make([]int, len(nums))
|
||||||
|
|
||||||
|
addOne := func(index int, value int) {
|
||||||
|
result[index] = value + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.ForEachConcurrent(nums, addOne, 4)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [2 3 4 5 6 7 8 9]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### <span id="ForEachWithBreak">ForEachWithBreak</span>
|
### <span id="ForEachWithBreak">ForEachWithBreak</span>
|
||||||
|
|
||||||
<p>遍历切片的元素并为每个元素调用iteratee函数,当iteratee函数返回false时,终止遍历。</p>
|
<p>遍历切片的元素并为每个元素调用iteratee函数,当iteratee函数返回false时,终止遍历。</p>
|
||||||
@@ -1240,10 +1326,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="IntSlice">IntSlice (已弃用: 使用 go1.18+泛型代替)</span>
|
### <span id="IntSlice">IntSlice</span>
|
||||||
|
|
||||||
<p>将接口切片转换为int切片</p>
|
<p>将接口切片转换为int切片</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用go1.18+泛型代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -1269,10 +1357,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="InterfaceSlice">InterfaceSlice(已弃用: 使用 go1.18+泛型代替)</span>
|
### <span id="InterfaceSlice">InterfaceSlice</span>
|
||||||
|
|
||||||
<p>将值转换为接口切片</p>
|
<p>将值转换为接口切片</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用go1.18+泛型代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -1437,7 +1527,7 @@ func main() {
|
|||||||
|
|
||||||
### <span id="Map">Map</span>
|
### <span id="Map">Map</span>
|
||||||
|
|
||||||
<p>对slice中的每个元素执行map函数以创建一个新切片</p>
|
<p>对slice中的每个元素执行map函数以创建一个新切片。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -1469,6 +1559,38 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="MapConcurrent">MapConcurrent</span>
|
||||||
|
|
||||||
|
<p>对slice并发执行map操作。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
|
||||||
|
result := slice.MapConcurrent(nums, func(_, n int) int {
|
||||||
|
return n * n
|
||||||
|
}, 4)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 4 9 16 25 36]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="FilterMap">FilterMap</span>
|
### <span id="FilterMap">FilterMap</span>
|
||||||
|
|
||||||
<p>返回一个将filter和map操作应用于给定切片的切片。 iteratee回调函数应该返回两个值:1,结果值。2,结果值是否应该被包含在返回的切片中。</p>
|
<p>返回一个将filter和map操作应用于给定切片的切片。 iteratee回调函数应该返回两个值:1,结果值。2,结果值是否应该被包含在返回的切片中。</p>
|
||||||
@@ -1543,6 +1665,8 @@ func main() {
|
|||||||
|
|
||||||
<p>合并多个切片(不会消除重复元素).</p>
|
<p>合并多个切片(不会消除重复元素).</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用`Concat`代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -1602,7 +1726,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="Reduce">Reduce</span>
|
### <span id="Reduce">Reduce</span>
|
||||||
|
|
||||||
<p>将切片中的元素依次运行iteratee函数,返回运行结果(废弃:建议使用ReduceBy)</p>
|
<p>将切片中的元素依次运行iteratee函数,返回运行结果。</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用`ReduceBy`代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
@@ -1634,6 +1760,38 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="ReduceConcurrent">ReduceConcurrent</span>
|
||||||
|
|
||||||
|
<p>对切片元素执行并发reduce操作。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
|
||||||
|
result := slice.ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
|
||||||
|
return agg + item
|
||||||
|
}, 1)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 55
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="ReduceBy">ReduceBy</span>
|
### <span id="ReduceBy">ReduceBy</span>
|
||||||
|
|
||||||
<p>对切片元素执行reduce操作。</p>
|
<p>对切片元素执行reduce操作。</p>
|
||||||
@@ -2128,10 +2286,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="StringSlice">StringSlice(已弃用: 使用 go1.18+泛型代替)</span>
|
### <span id="StringSlice">StringSlice</span>
|
||||||
|
|
||||||
<p>将接口切片转换为字符串切片</p>
|
<p>将接口切片转换为字符串切片</p>
|
||||||
|
|
||||||
|
> ⚠️ 本函数已弃用,使用go1.18+泛型代替。
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -2280,12 +2440,12 @@ func main() {
|
|||||||
|
|
||||||
### <span id="UniqueBy">UniqueBy</span>
|
### <span id="UniqueBy">UniqueBy</span>
|
||||||
|
|
||||||
<p>对切片的每个元素调用iteratee函数,然后删除重复元素</p>
|
<p>根据迭代函数返回的值,从输入切片中移除重复元素。此函数保持元素的顺序。</p>
|
||||||
|
|
||||||
<b>函数签名:</b>
|
<b>函数签名:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T
|
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/UR323iZLDpv)</span></b>
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/UR323iZLDpv)</span></b>
|
||||||
@@ -2305,7 +2465,114 @@ func main() {
|
|||||||
fmt.Println(result)
|
fmt.Println(result)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [1 2 0]
|
// [1 2 3]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="UniqueByComparator">UniqueByComparator</span>
|
||||||
|
|
||||||
|
<p>使用提供的比较器函数从输入切片中移除重复元素。此函数保持元素的顺序。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
uniqueNums := slice.UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
|
||||||
|
return item == other
|
||||||
|
})
|
||||||
|
|
||||||
|
caseInsensitiveStrings := slice.UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool {
|
||||||
|
return strings.ToLower(item) == strings.ToLower(other)
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(uniqueNums)
|
||||||
|
fmt.Println(caseInsensitiveStrings)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5 6]
|
||||||
|
// [apple banana cherry date]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="UniqueByConcurrent">UniqueByConcurrent</span>
|
||||||
|
|
||||||
|
<p>并发的从输入切片中移除重复元素,结果保持元素的顺序。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
|
||||||
|
comparator := func(item int, other int) bool { return item == other }
|
||||||
|
|
||||||
|
result := slice.UniqueByConcurrent(nums, comparator, 4)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5 6 7]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="UniqueByField">UniqueByField</span>
|
||||||
|
|
||||||
|
<p>根据struct字段对struct切片去重复。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func UniqueByField[T any](slice []T, field string) ([]T, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/6cifcZSPIGu)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []User{
|
||||||
|
{ID: 1, Name: "a"},
|
||||||
|
{ID: 2, Name: "b"},
|
||||||
|
{ID: 1, Name: "c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := slice.UniqueByField(users, "ID")
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [{1 a} {2 b}]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -2581,7 +2848,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 (
|
||||||
@@ -2591,13 +2858,100 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
strs := []string{"a", "b", "a", "c", "d", "a"}
|
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||||
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||||
|
|
||||||
fmt.Println(modifiedStrs)
|
fmt.Println(modifiedStrs)
|
||||||
fmt.Println(count)
|
fmt.Println(count)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [ 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>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/yLYcBTyeQIz)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
even := func(n int) bool { return n%2 == 0 }
|
||||||
|
|
||||||
|
resultEven, resultAfterFirstEven := slice.Break(nums, even)
|
||||||
|
|
||||||
|
fmt.Println(resultEven)
|
||||||
|
fmt.Println(resultAfterFirstEven)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1]
|
||||||
|
// [2 3 4 5]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<span id="RightPadding">RightPadding</span>
|
||||||
|
|
||||||
|
<p>在切片的右部添加元素。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/0_2rlLEMBXL)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
padded := slice.RightPadding(nums, 0, 3)
|
||||||
|
fmt.Println(padded)
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5 0 0 0]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<span id="LeftPadding">LeftPadding</span>
|
||||||
|
|
||||||
|
<p>在切片的左部添加元素。</p>
|
||||||
|
|
||||||
|
<b>函数签名:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jlQVoelLl2k)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
padded := slice.LeftPadding(nums, 0, 3)
|
||||||
|
fmt.Println(padded)
|
||||||
|
// Output:
|
||||||
|
// [0 0 0 1 2 3 4 5]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -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
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -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
|
||||||
@@ -953,9 +856,11 @@ func main() {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### <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
|
||||||
@@ -1024,7 +929,7 @@ func main() {
|
|||||||
|
|
||||||
### <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
|
||||||
@@ -1045,7 +950,7 @@ 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")
|
||||||
@@ -1088,7 +993,7 @@ func main() {
|
|||||||
|
|
||||||
### <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
|
||||||
@@ -1109,7 +1014,7 @@ 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")
|
||||||
@@ -1132,11 +1037,11 @@ func main() {
|
|||||||
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:
|
||||||
@@ -1148,4 +1053,67 @@ func main() {
|
|||||||
// 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
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
|
|
||||||
- [New](#New)
|
- [New](#New)
|
||||||
- [FromSlice](#FromSlice)
|
- [FromSlice](#FromSlice)
|
||||||
- [Values](#Values)
|
- [Values<sup>deprecated</sup>](#Values)
|
||||||
- [Add](#Add)
|
- [Add](#Add)
|
||||||
- [AddIfNotExist](#AddIfNotExist)
|
- [AddIfNotExist](#AddIfNotExist)
|
||||||
- [AddIfNotExistBy](#AddIfNotExistBy)
|
- [AddIfNotExistBy](#AddIfNotExistBy)
|
||||||
@@ -102,10 +102,11 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="Values">Values<sup>deprecated</sup></span>
|
### <span id="Values">Values</span>
|
||||||
|
|
||||||
<p>Return slice of all set data.<br>
|
<p>Return slice of all set data.</p>
|
||||||
The <a href='#ToSlice'>ToSlice()</a> function provides the same functionality as Values and returns a slice containing all values of the set.</p>
|
|
||||||
|
> ⚠️ This function is deprecated. use `ToSlice` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
|||||||
@@ -167,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>
|
||||||
|
|
||||||
@@ -974,7 +974,7 @@ func main() {
|
|||||||
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
|
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/r0hPmKWhsgf)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -1033,7 +1033,7 @@ func main() {
|
|||||||
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
|
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/teMXnCsdSEw)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ import (
|
|||||||
- [Before](#Before)
|
- [Before](#Before)
|
||||||
- [CurryFn](#CurryFn)
|
- [CurryFn](#CurryFn)
|
||||||
- [Compose](#Compose)
|
- [Compose](#Compose)
|
||||||
- [Debounced](#Debounced)
|
- [Debounce](#Debounce)
|
||||||
|
- [Debounced<sup>deprecated</sup>](#Debounced)
|
||||||
- [Delay](#Delay)
|
- [Delay](#Delay)
|
||||||
- [Schedule](#Schedule)
|
- [Schedule](#Schedule)
|
||||||
- [Pipeline](#Pipeline)
|
- [Pipeline](#Pipeline)
|
||||||
@@ -40,6 +41,8 @@ import (
|
|||||||
- [Xnor](#Xnor)
|
- [Xnor](#Xnor)
|
||||||
- [Nand](#Nand)
|
- [Nand](#Nand)
|
||||||
- [AcceptIf](#AcceptIf)
|
- [AcceptIf](#AcceptIf)
|
||||||
|
- [Throttle](#Throttle)
|
||||||
|
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -191,11 +194,59 @@ func main() {
|
|||||||
// ABCDE
|
// ABCDE
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
### <span id="Debounce">Debounce</span>
|
||||||
|
|
||||||
|
<p>Creates a debounced version of the provided function. The debounced function will only invoke the original function after the specified delay has passed since the last time it was invoked. It also supports canceling the debounce.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
callCount := 0
|
||||||
|
fn := func() {
|
||||||
|
callCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
debouncedFn()
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
fmt.Println(callCount)
|
||||||
|
|
||||||
|
debouncedFn()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
fmt.Println(callCount)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 1
|
||||||
|
// 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="Debounced">Debounced</span>
|
### <span id="Debounced">Debounced</span>
|
||||||
|
|
||||||
<p>Creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.</p>
|
<p>Creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use `Debounce` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -423,7 +474,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 +511,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 +546,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 +586,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 +628,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 +665,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 +694,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 +702,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 +714,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 {
|
||||||
@@ -691,5 +740,46 @@ func main() {
|
|||||||
// 0
|
// 0
|
||||||
// false
|
// false
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Throttle">Throttle</span>
|
||||||
|
|
||||||
|
<p>Throttle creates a throttled version of the provided function. The returned function guarantees that it will only be invoked at most once per interval.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Throttle(fn func(), interval time.Duration) func()
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
fn := func() {
|
||||||
|
callCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
throttledFn := function.Throttle(fn, 1*time.Second)
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
throttledFn()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
fmt.Println(callCount)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 1
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -44,6 +44,9 @@ import (
|
|||||||
- [Minus](#Minus)
|
- [Minus](#Minus)
|
||||||
- [IsDisjoint](#IsDisjoint)
|
- [IsDisjoint](#IsDisjoint)
|
||||||
- [HasKey](#HasKey)
|
- [HasKey](#HasKey)
|
||||||
|
- [MapToStruct](#MapToStruct)
|
||||||
|
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
|
||||||
|
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
|
||||||
- [NewConcurrentMap](#NewConcurrentMap)
|
- [NewConcurrentMap](#NewConcurrentMap)
|
||||||
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
- [ConcurrentMap_Get](#ConcurrentMap_Get)
|
||||||
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
- [ConcurrentMap_Set](#ConcurrentMap_Set)
|
||||||
@@ -52,6 +55,7 @@ import (
|
|||||||
- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete)
|
- [ConcurrentMap_GetAndDelete](#ConcurrentMap_GetAndDelete)
|
||||||
- [ConcurrentMap_Has](#ConcurrentMap_Has)
|
- [ConcurrentMap_Has](#ConcurrentMap_Has)
|
||||||
- [ConcurrentMap_Range](#ConcurrentMap_Range)
|
- [ConcurrentMap_Range](#ConcurrentMap_Range)
|
||||||
|
- [GetOrSet](#GetOrSet)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -992,6 +996,144 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="MapToStruct">MapToStruct</span>
|
||||||
|
|
||||||
|
<p>Converts map to struct</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func MapToStruct(m map[string]any, structObj any) error
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/7wYyVfX38Dp)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
personReqMap := map[string]any{
|
||||||
|
"name": "Nothin",
|
||||||
|
"max_age": 35,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersonReq struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MaxAge int `json:"max_age"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
|
}
|
||||||
|
var personReq PersonReq
|
||||||
|
_ = maputil.MapToStruct(personReqMap, &personReq)
|
||||||
|
fmt.Println(personReq)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {Nothin 35 1 10}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToSortedSlicesDefault">ToSortedSlicesDefault</span>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Translate the key and value of the map into two slices that are sorted in ascending order according to the key’s value, with the position of the elements in the value slice corresponding to the key.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/43gEM2po-qy)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, values := maputil.ToSortedSlicesDefault(m)
|
||||||
|
|
||||||
|
fmt.Println(keys)
|
||||||
|
fmt.Println(values)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2 3]
|
||||||
|
// [a b c]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="ToSortedSlicesWithComparator">ToSortedSlicesWithComparator</span>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Translate the key and value of the map into two slices that are sorted according to a custom sorting rule defined by a comparator function based on the key's value, with the position of the elements in the value slice corresponding to the key.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/0nlPo6YLdt3)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m1 := map[time.Time]string{
|
||||||
|
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
|
||||||
|
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
|
||||||
|
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys1, values1 := maputil.ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
|
||||||
|
return a.Before(b)
|
||||||
|
})
|
||||||
|
|
||||||
|
m2 := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
keys2, values2 := maputil.ToSortedSlicesWithComparator(m2, func(a, b int) bool {
|
||||||
|
return a > b
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(keys2)
|
||||||
|
fmt.Println(values2)
|
||||||
|
|
||||||
|
fmt.Println(keys1)
|
||||||
|
fmt.Println(values1)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [3 2 1]
|
||||||
|
// [c b a]
|
||||||
|
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
|
||||||
|
// [yesterday today tomorrow]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="NewConcurrentMap">NewConcurrentMap</span>
|
### <span id="NewConcurrentMap">NewConcurrentMap</span>
|
||||||
|
|
||||||
<p>ConcurrentMap is like map, but is safe for concurrent use by multiple goroutines.</p>
|
<p>ConcurrentMap is like map, but is safe for concurrent use by multiple goroutines.</p>
|
||||||
@@ -1055,15 +1197,15 @@ func main() {
|
|||||||
|
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
||||||
fmt.Println(val, ok)
|
fmt.Println(val, ok)
|
||||||
wg2.Done()
|
wg2.Done()
|
||||||
}(j)
|
}(j)
|
||||||
}
|
}
|
||||||
wg2.Wait()
|
wg2.Wait()
|
||||||
|
|
||||||
// output: (order may change)
|
// output: (order may change)
|
||||||
// 1 true
|
// 1 true
|
||||||
@@ -1110,15 +1252,15 @@ func main() {
|
|||||||
|
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
val, ok := cm.Get(fmt.Sprintf("%d", n))
|
||||||
fmt.Println(val, ok)
|
fmt.Println(val, ok)
|
||||||
wg2.Done()
|
wg2.Done()
|
||||||
}(j)
|
}(j)
|
||||||
}
|
}
|
||||||
wg2.Wait()
|
wg2.Wait()
|
||||||
|
|
||||||
// output: (order may change)
|
// output: (order may change)
|
||||||
// 1 true
|
// 1 true
|
||||||
@@ -1208,7 +1350,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
cm.Delete(fmt.Sprintf("%d", n))
|
cm.Delete(fmt.Sprintf("%d", n))
|
||||||
@@ -1255,7 +1397,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n))
|
val, ok := cm.GetAndDelete(fmt.Sprintf("%d", n))
|
||||||
@@ -1307,7 +1449,7 @@ func main() {
|
|||||||
wg1.Wait()
|
wg1.Wait()
|
||||||
|
|
||||||
var wg2 sync.WaitGroup
|
var wg2 sync.WaitGroup
|
||||||
wg2.Add(5)
|
wg2.Add(5)
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
go func(n int) {
|
go func(n int) {
|
||||||
ok := cm.Has(fmt.Sprintf("%d", n))
|
ok := cm.Has(fmt.Sprintf("%d", n))
|
||||||
@@ -1360,3 +1502,77 @@ func main() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="GetOrSet">GetOrSet</span>
|
||||||
|
|
||||||
|
<p>Returns value of the given key or set the given value value if not present.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/IVQwO1OkEJC)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
}
|
||||||
|
|
||||||
|
result1 := maputil.GetOrSet(m, 1, "1")
|
||||||
|
result2 := maputil.GetOrSet(m, 2, "b")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// a
|
||||||
|
// b
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="SortByKey">SortByKey</span>
|
||||||
|
|
||||||
|
<p>Sorts the map by its keys and returns a new map with sorted keys.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func SortByKey[K constraints.Ordered, V any](m map[K]V) (sortedKeysMap map[K]V)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/maputil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
m := map[int]string{
|
||||||
|
3: "c",
|
||||||
|
1: "a",
|
||||||
|
4: "d",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
|
||||||
|
result := maputil.SortByKey(m)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// map[1:a 2:b 3:c 4:d]
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -508,7 +508,7 @@ func main() {
|
|||||||
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/8hOeSADZPCo)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -544,7 +544,7 @@ func main() {
|
|||||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wy5bYEyUKKG)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -577,10 +577,10 @@ func main() {
|
|||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/vbCBrQHZEED)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -613,10 +613,10 @@ func main() {
|
|||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Qk9KPd2IdDb)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -1130,7 +1130,7 @@ func main() {
|
|||||||
|
|
||||||
### <span id="Div">Div</span>
|
### <span id="Div">Div</span>
|
||||||
|
|
||||||
<p>returns the result of x divided by y.</p>
|
<p>Returns the result of x divided by y.</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -1138,7 +1138,7 @@ func main() {
|
|||||||
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
|
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/WLxDdGXXYat)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -624,7 +624,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="HttpGet">HttpGet</span>
|
### <span id="HttpGet">HttpGet</span>
|
||||||
|
|
||||||
<p>Send http get request. (Deprecated: use SendRequest for replacement)</p>
|
<p>Send http get request.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use `SendRequest` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -666,7 +668,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="HttpPost">HttpPost</span>
|
### <span id="HttpPost">HttpPost</span>
|
||||||
|
|
||||||
<p>Send http post request.(Deprecated: use SendRequest for replacement)</p>
|
<p>Send http post request.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use `SendRequest` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -713,7 +717,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="HttpPut">HttpPut</span>
|
### <span id="HttpPut">HttpPut</span>
|
||||||
|
|
||||||
<p>Send http put request. (Deprecated: use SendRequest for replacement)</p>
|
<p>Send http put request.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use `SendRequest` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -763,7 +769,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="HttpDelete">HttpDelete</span>
|
### <span id="HttpDelete">HttpDelete</span>
|
||||||
|
|
||||||
<p>Send http delete request. (Deprecated: use SendRequest for replacement)</p>
|
<p>Send http delete request.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use `SendRequest` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -802,7 +810,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="HttpPatch">HttpPatch</span>
|
### <span id="HttpPatch">HttpPatch</span>
|
||||||
|
|
||||||
<p>Send http patch request. (Deprecated: use SendRequest for replacement)</p>
|
<p>Send http patch request.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use `SendRequest` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import (
|
|||||||
|
|
||||||
- [Of](#Of)
|
- [Of](#Of)
|
||||||
- [Unwrap](#Unwrap)
|
- [Unwrap](#Unwrap)
|
||||||
- [UnwarpOr](#UnwarpOr)
|
- [UnwrapOr](#UnwrapOr)
|
||||||
- [UnwarpOrDefault](#UnwarpOrDefault)
|
- [UnwrapOrDefault](#UnwrapOrDefault)
|
||||||
- [ExtractPointer](#ExtractPointer)
|
- [ExtractPointer](#ExtractPointer)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
@@ -103,13 +103,13 @@ func main() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### <span id="UnwarpOr">UnwarpOr</span>
|
### <span id="UnwrapOr">UnwrapOr</span>
|
||||||
|
|
||||||
<p>Returns the value from the pointer or fallback if the pointer is nil.</p>
|
<p>Returns the value from the pointer or fallback if the pointer is nil.</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
```go
|
```go
|
||||||
UnwarpOr[T any](p *T, fallback T) T
|
UnwrapOr[T any](p *T, fallback T) T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/mmNaLC38W8C)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/mmNaLC38W8C)</span></b>
|
||||||
@@ -129,10 +129,10 @@ func main() {
|
|||||||
var c *int
|
var c *int
|
||||||
var d *string
|
var d *string
|
||||||
|
|
||||||
result1 := pointer.UnwarpOr(&a, 456)
|
result1 := pointer.UnwrapOr(&a, 456)
|
||||||
result2 := pointer.UnwarpOr(&b, "abc")
|
result2 := pointer.UnwrapOr(&b, "abc")
|
||||||
result3 := pointer.UnwarpOr(c, 456)
|
result3 := pointer.UnwrapOr(c, 456)
|
||||||
result4 := pointer.UnwarpOr(d, "def")
|
result4 := pointer.UnwrapOr(d, "def")
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
@@ -148,13 +148,13 @@ func main() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### <span id="UnwarpOrDefault">UnwarpOrDefault</span>
|
### <span id="UnwrapOrDefault">UnwrapOrDefault</span>
|
||||||
|
|
||||||
<p>Returns the value from the pointer or the default value if the pointer is nil.</p>
|
<p>Returns the value from the pointer or the default value if the pointer is nil.</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
```go
|
```go
|
||||||
UnwarpOrDefault[T any](p *T) T
|
UnwrapOrDefault[T any](p *T) T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ZnGIHf8_o4E)</span></b>
|
||||||
@@ -174,10 +174,10 @@ func main() {
|
|||||||
var c *int
|
var c *int
|
||||||
var d *string
|
var d *string
|
||||||
|
|
||||||
result1 := pointer.UnwarpOrDefault(&a)
|
result1 := pointer.UnwrapOrDefault(&a)
|
||||||
result2 := pointer.UnwarpOrDefault(&b)
|
result2 := pointer.UnwrapOrDefault(&b)
|
||||||
result3 := pointer.UnwarpOrDefault(c)
|
result3 := pointer.UnwrapOrDefault(c)
|
||||||
result4 := pointer.UnwarpOrDefault(d)
|
result4 := pointer.UnwrapOrDefault(d)
|
||||||
|
|
||||||
fmt.Println(result1)
|
fmt.Println(result1)
|
||||||
fmt.Println(result2)
|
fmt.Println(result2)
|
||||||
|
|||||||
@@ -25,15 +25,21 @@ import (
|
|||||||
- [RandBytes](#RandBytes)
|
- [RandBytes](#RandBytes)
|
||||||
- [RandInt](#RandInt)
|
- [RandInt](#RandInt)
|
||||||
- [RandString](#RandString)
|
- [RandString](#RandString)
|
||||||
|
- [RandFromGivenSlice](#RandFromGivenSlice)
|
||||||
|
- [RandSliceFromGivenSlice](#RandSliceFromGivenSlice)
|
||||||
- [RandUpper](#RandUpper)
|
- [RandUpper](#RandUpper)
|
||||||
- [RandLower](#RandLower)
|
- [RandLower](#RandLower)
|
||||||
- [RandNumeral](#RandNumeral)
|
- [RandNumeral](#RandNumeral)
|
||||||
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
- [RandNumeralOrLetter](#RandNumeralOrLetter)
|
||||||
- [RandSymbolChar](#RandSymbolChar)
|
- [RandSymbolChar](#RandSymbolChar)
|
||||||
- [UUIdV4](#UUIdV4)
|
- [UUIdV4](#UUIdV4)
|
||||||
|
- [RandIntSlice](#RandIntSlice)
|
||||||
- [RandUniqueIntSlice](#RandUniqueIntSlice)
|
- [RandUniqueIntSlice](#RandUniqueIntSlice)
|
||||||
- [RandFloat](#RandFloat)
|
- [RandFloat](#RandFloat)
|
||||||
- [RandFloats](#RandFloats)
|
- [RandFloats](#RandFloats)
|
||||||
|
- [RandStringSlice](#RandStringSlice)
|
||||||
|
- [RandBool](#RandBool)
|
||||||
|
- [RandBoolSlice](#RandBoolSlice)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -117,6 +123,60 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="RandFromGivenSlice">RandFromGivenSlice</span>
|
||||||
|
|
||||||
|
<p>Generate a random element from given slice.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandFromGivenSlice[T any](slice []T) T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
randomSet := []any{"a", 8, "hello", true, 1.1}
|
||||||
|
randElm := random.RandFromGivenSlice(randomSet)
|
||||||
|
fmt.Println(randElm)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RandSliceFromGivenSlice">RandSliceFromGivenSlice</span>
|
||||||
|
|
||||||
|
<p>Generate a random slice of length num from given slice.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon", "mango", "nectarine", "orange"}
|
||||||
|
chosen3goods := random.RandSliceFromGivenSlice(goods, 3, false)
|
||||||
|
fmt.Println(chosen3goods)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="RandUpper">RandUpper</span>
|
### <span id="RandUpper">RandUpper</span>
|
||||||
|
|
||||||
<p>Generate a random upper case string</p>
|
<p>Generate a random upper case string</p>
|
||||||
@@ -276,15 +336,41 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="RandIntSlice">RandIntSlice</span>
|
||||||
|
|
||||||
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
|
<p>Generate a slice of random int. Number range in [min, max)</p>
|
||||||
|
|
||||||
<p>Generate a slice of random int of length n that do not repeat.</p>
|
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func RandUniqueIntSlice(n, min, max int) []int
|
func RandIntSlice(length, min, max int) []int
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result := random.RandIntSlice(5, 0, 10)
|
||||||
|
fmt.Println(result) //[1 4 7 1 5] (random)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="RandUniqueIntSlice">RandUniqueIntSlice</span>
|
||||||
|
|
||||||
|
<p>Generate a slice of random int of length that do not repeat. Number range in [min, max)</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandUniqueIntSlice(length, min, max int) []int
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uBkRSOz73Ec)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uBkRSOz73Ec)</span></b>
|
||||||
@@ -331,12 +417,12 @@ func main() {
|
|||||||
|
|
||||||
### <span id="RandFloats">RandFloats</span>
|
### <span id="RandFloats">RandFloats</span>
|
||||||
|
|
||||||
<p>Generate a slice of random float64 numbers of length n that do not repeat.</p>
|
<p>Generate a slice of random float64 numbers of length n that do not repeat. Number range in [min, max)</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func RandFloats(n int, min, max float64, precision int) []float64
|
func RandFloats(length int, min, max float64, precision int) []float64
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
|
||||||
@@ -353,4 +439,86 @@ func main() {
|
|||||||
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
|
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
|
||||||
fmt.Println(floatNumbers) //[3.42 3.99 1.3 2.38 4.23] (random)
|
fmt.Println(floatNumbers) //[3.42 3.99 1.3 2.38 4.23] (random)
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### <span id="RandStringSlice">RandStringSlice</span>
|
||||||
|
|
||||||
|
<p>Generate a slice of random string of length strLen based on charset. chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters random.Letters, random.SymbolChars, random.AllChars. or a combination of them.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandStringSlice(charset string, sliceLen, strLen int) []string
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
strs := random.RandStringSlice(random.Letters, 4, 6)
|
||||||
|
fmt.Println(strs)
|
||||||
|
|
||||||
|
// output random string slice like below:
|
||||||
|
//[CooSMq RUFjDz FAeMPf heRyGv]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RandBool">RandBool</span>
|
||||||
|
|
||||||
|
<p>Generate a random boolean value (true or false).</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandBool() bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result := random.RandBool()
|
||||||
|
fmt.Println(result) // true or false (random)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="RandBoolSlice">RandBoolSlice</span>
|
||||||
|
|
||||||
|
<p>Generates a random boolean slice of specified length.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RandBoolSlice(length int) []bool
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/random"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
result := random.RandBoolSlice(2)
|
||||||
|
fmt.Println(result) // [true false] (random)
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -331,7 +331,7 @@ func main() {
|
|||||||
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
|
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jIm_o2vb5Y4)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -361,7 +361,7 @@ func main() {
|
|||||||
return errors.New("error occurs")
|
return errors.New("error occurs")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
|
err := retry.Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -384,7 +384,7 @@ func main() {
|
|||||||
func RetryWithLinearBackoff(interval time.Duration) Option
|
func RetryWithLinearBackoff(interval time.Duration) Option
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/nk2XRmagfVF)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
@@ -429,7 +429,7 @@ func main() {
|
|||||||
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
|
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:</b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/xp1avQmn16X)</span></b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ Package slice implements some functions to manipulate slice.
|
|||||||
|
|
||||||
## Source:
|
## Source:
|
||||||
|
|
||||||
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
|
- [https://github.com/duke-git/lancet/blob/main/slice/slice.go](https://github.com/duke-git/lancet/blob/main/slice/slice.go)
|
||||||
|
- [https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go](https://github.com/duke-git/lancet/blob/main/slice/slice_concurrent.go)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ import (
|
|||||||
- [EqualWith](#EqualWith)
|
- [EqualWith](#EqualWith)
|
||||||
- [Every](#Every)
|
- [Every](#Every)
|
||||||
- [Filter](#Filter)
|
- [Filter](#Filter)
|
||||||
|
- [FilterConcurrent](#FilterConcurrent)
|
||||||
- [Find<sup>deprecated</sup>](#Find)
|
- [Find<sup>deprecated</sup>](#Find)
|
||||||
- [FindBy](#FindBy)
|
- [FindBy](#FindBy)
|
||||||
- [FindLast<sup>deprecated</sup>](#FindLast)
|
- [FindLast<sup>deprecated</sup>](#FindLast)
|
||||||
@@ -51,6 +53,7 @@ import (
|
|||||||
- [Flatten](#Flatten)
|
- [Flatten](#Flatten)
|
||||||
- [FlattenDeep](#FlattenDeep)
|
- [FlattenDeep](#FlattenDeep)
|
||||||
- [ForEach](#ForEach)
|
- [ForEach](#ForEach)
|
||||||
|
- [ForEachConcurrent](#ForEachConcurrent)
|
||||||
- [ForEachWithBreak](#ForEachWithBreak)
|
- [ForEachWithBreak](#ForEachWithBreak)
|
||||||
- [GroupBy](#GroupBy)
|
- [GroupBy](#GroupBy)
|
||||||
- [GroupWith](#GroupWith)
|
- [GroupWith](#GroupWith)
|
||||||
@@ -61,11 +64,13 @@ import (
|
|||||||
- [IndexOf](#IndexOf)
|
- [IndexOf](#IndexOf)
|
||||||
- [LastIndexOf](#LastIndexOf)
|
- [LastIndexOf](#LastIndexOf)
|
||||||
- [Map](#Map)
|
- [Map](#Map)
|
||||||
|
- [MapConcurrent](#MapConcurrent)
|
||||||
- [FilterMap](#FilterMap)
|
- [FilterMap](#FilterMap)
|
||||||
- [FlatMap](#FlatMap)
|
- [FlatMap](#FlatMap)
|
||||||
- [Merge](#Merge)
|
- [Merge](#Merge)
|
||||||
- [Reverse](#Reverse)
|
- [Reverse](#Reverse)
|
||||||
- [Reduce<sup>deprecated</sup>](#Reduce)
|
- [Reduce<sup>deprecated</sup>](#Reduce)
|
||||||
|
- [ReduceConcurrent](#ReduceConcurrent)
|
||||||
- [ReduceBy](#ReduceBy)
|
- [ReduceBy](#ReduceBy)
|
||||||
- [ReduceRight](#ReduceRight)
|
- [ReduceRight](#ReduceRight)
|
||||||
- [Replace](#Replace)
|
- [Replace](#Replace)
|
||||||
@@ -86,6 +91,9 @@ import (
|
|||||||
- [ToSlicePointer](#ToSlicePointer)
|
- [ToSlicePointer](#ToSlicePointer)
|
||||||
- [Unique](#Unique)
|
- [Unique](#Unique)
|
||||||
- [UniqueBy](#UniqueBy)
|
- [UniqueBy](#UniqueBy)
|
||||||
|
- [UniqueByComparator](#UniqueByComparator)
|
||||||
|
- [UniqueByField](#UniqueByField)
|
||||||
|
- [UniqueByConcurrent](#UniqueByConcurrent)
|
||||||
- [Union](#Union)
|
- [Union](#Union)
|
||||||
- [UnionBy](#UnionBy)
|
- [UnionBy](#UnionBy)
|
||||||
- [UpdateAt](#UpdateAt)
|
- [UpdateAt](#UpdateAt)
|
||||||
@@ -93,6 +101,10 @@ import (
|
|||||||
- [KeyBy](#KeyBy)
|
- [KeyBy](#KeyBy)
|
||||||
- [Join](#Join)
|
- [Join](#Join)
|
||||||
- [Partition](#Partition)
|
- [Partition](#Partition)
|
||||||
|
- [SetToDefaultIf](#SetToDefaultIf)
|
||||||
|
- [Break](#Break)
|
||||||
|
- [RightPadding](#RightPadding)
|
||||||
|
- [LeftPadding](#LeftPadding)
|
||||||
|
|
||||||
<div STYLE="page-break-after: always;"></div>
|
<div STYLE="page-break-after: always;"></div>
|
||||||
|
|
||||||
@@ -317,12 +329,12 @@ func main() {
|
|||||||
|
|
||||||
### <span id="Concat">Concat</span>
|
### <span id="Concat">Concat</span>
|
||||||
|
|
||||||
<p>Creates a new slice concatenating slice with any additional slices.</p>
|
<p>Concat creates a new slice concatenating slice with any additional slices.</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func Concat[T any](slice []T, slices ...[]T) []T
|
func Concat[T any](slices ...[]T) []T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/gPt-q7zr5mk)</span></b>
|
||||||
@@ -890,10 +902,46 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="Find">Find(deprecated: use FindBy)</span>
|
### <span id="FilterConcurrent">FilterConcurrent</span>
|
||||||
|
|
||||||
|
<p>Applies the provided filter function `predicate` to each element of the input slice concurrently.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
isEven := func(i, num int) bool {
|
||||||
|
return num%2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
result := slice.FilterConcurrent(nums, isEven, 2)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [2 4]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="Find">Find</span>
|
||||||
|
|
||||||
<p>Iterates over elements of slice, returning the first one that passes a truth test on function.</p>
|
<p>Iterates over elements of slice, returning the first one that passes a truth test on function.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use `FindBy` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -962,10 +1010,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="FindLast">FindLast(deprecated: use FindLastBy)</span>
|
### <span id="FindLast">FindLast</span>
|
||||||
|
|
||||||
<p>iterates over elements of slice from end to begin, returning the last one that passes a truth test on function.</p>
|
<p>iterates over elements of slice from end to begin, returning the last one that passes a truth test on function.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use `FindLastBy` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -1129,6 +1179,42 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="ForEachConcurrent">ForEachConcurrent</span>
|
||||||
|
|
||||||
|
<p>Applies the iteratee function to each item in the slice concurrently.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||||
|
|
||||||
|
result := make([]int, len(nums))
|
||||||
|
|
||||||
|
addOne := func(index int, value int) {
|
||||||
|
result[index] = value + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.ForEachConcurrent(nums, addOne, 4)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [2 3 4 5 6 7 8 9]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="ForEachWithBreak">ForEachWithBreak</span>
|
### <span id="ForEachWithBreak">ForEachWithBreak</span>
|
||||||
|
|
||||||
<p>Iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop.</p>
|
<p>Iterates over elements of slice and invokes function for each element, when iteratee return false, will break the for each loop.</p>
|
||||||
@@ -1237,10 +1323,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="IntSlice">IntSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
|
### <span id="IntSlice">IntSlice</span>
|
||||||
|
|
||||||
<p>Convert interface slice to int slice.</p>
|
<p>Convert interface slice to int slice.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. Use generic feature of go1.18+ for replacement.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -1266,10 +1354,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="InterfaceSlice">InterfaceSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
|
### <span id="InterfaceSlice">InterfaceSlice</span>
|
||||||
|
|
||||||
<p>Convert value to interface slice.</p>
|
<p>Convert value to interface slice.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. Use generic feature of go1.18+ for replacement.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -1466,6 +1556,36 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="MapConcurrent">MapConcurrent</span>
|
||||||
|
|
||||||
|
<p>Applies the iteratee function to each item in the slice by concrrent.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
|
||||||
|
result := slice.MapConcurrent(nums, func(_, n int) int { return n * n }, 4)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 4 9 16 25 36]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### <span id="FilterMap">FilterMap</span>
|
### <span id="FilterMap">FilterMap</span>
|
||||||
|
|
||||||
<p>Returns a slice which apply both filtering and mapping to the given slice. iteratee callback function should returntwo values: 1, mapping result. 2, whether the result element should be included or not.</p>
|
<p>Returns a slice which apply both filtering and mapping to the given slice. iteratee callback function should returntwo values: 1, mapping result. 2, whether the result element should be included or not.</p>
|
||||||
@@ -1540,6 +1660,8 @@ func main() {
|
|||||||
|
|
||||||
<p>Merge all given slices into one slice.</p>
|
<p>Merge all given slices into one slice.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use `Concat` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -1599,7 +1721,9 @@ func main() {
|
|||||||
|
|
||||||
### <span id="Reduce">Reduce</span>
|
### <span id="Reduce">Reduce</span>
|
||||||
|
|
||||||
<p>Reduce slice.(Deprecated: use ReduceBy)</p>
|
<p>Reduce slice.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use `ReduceBy` instead.
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
@@ -1631,6 +1755,39 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <span id="ReduceConcurrent">ReduceConcurrent</span>
|
||||||
|
|
||||||
|
<p>Reduces the slice to a single value by applying the reducer function to each item in the slice concurrently.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
|
||||||
|
result := slice.ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
|
||||||
|
return agg + item
|
||||||
|
}, 1)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 55
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### <span id="ReduceBy">ReduceBy</span>
|
### <span id="ReduceBy">ReduceBy</span>
|
||||||
|
|
||||||
<p>Produces a value from slice by accumulating the result of each element as passed through the reducer function.</p>
|
<p>Produces a value from slice by accumulating the result of each element as passed through the reducer function.</p>
|
||||||
@@ -2125,10 +2282,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <span id="StringSlice">StringSlice (Deprecated: use generic feature of go1.18+ for replacement)</span>
|
### <span id="StringSlice">StringSlice</span>
|
||||||
|
|
||||||
<p>Convert interface slice to string slice.</p>
|
<p>Convert interface slice to string slice.</p>
|
||||||
|
|
||||||
|
> ⚠️ This function is deprecated. use generic feature of go1.18+ for replacement
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -2277,12 +2436,12 @@ func main() {
|
|||||||
|
|
||||||
### <span id="UniqueBy">UniqueBy</span>
|
### <span id="UniqueBy">UniqueBy</span>
|
||||||
|
|
||||||
<p>Call iteratee func with every item of slice, then remove duplicated.</p>
|
<p>Removes duplicate elements from the input slice based on the values returned by the iteratee function. this function maintains the order of the elements.</p>
|
||||||
|
|
||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T
|
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T
|
||||||
```
|
```
|
||||||
|
|
||||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/UR323iZLDpv)</span></b>
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/UR323iZLDpv)</span></b>
|
||||||
@@ -2302,7 +2461,114 @@ func main() {
|
|||||||
fmt.Println(result)
|
fmt.Println(result)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [1 2 0]
|
// [1 2 3]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="UniqueByComparator">UniqueByComparator</span>
|
||||||
|
|
||||||
|
<p>Removes duplicate elements from the input slice using the provided comparator function. The function maintains the order of the elements.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
uniqueNums := slice.UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
|
||||||
|
return item == other
|
||||||
|
})
|
||||||
|
|
||||||
|
caseInsensitiveStrings := slice.UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool {
|
||||||
|
return strings.ToLower(item) == strings.ToLower(other)
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(uniqueNums)
|
||||||
|
fmt.Println(caseInsensitiveStrings)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5 6]
|
||||||
|
// [apple banana cherry date]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="UniqueByConcurrent">UniqueByConcurrent</span>
|
||||||
|
|
||||||
|
<p>Removes duplicate elements from the slice by parallel.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
|
||||||
|
comparator := func(item int, other int) bool { return item == other }
|
||||||
|
|
||||||
|
result := slice.UniqueByConcurrent(nums,comparator, 4)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5 6 7]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### <span id="UniqueByField">UniqueByField</span>
|
||||||
|
|
||||||
|
<p>Remove duplicate elements in struct slice by struct field.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func UniqueByField[T any](slice []T, field string) ([]T, error)
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Runs](https://go.dev/play/p/6cifcZSPIGu)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []User{
|
||||||
|
{ID: 1, Name: "a"},
|
||||||
|
{ID: 2, Name: "b"},
|
||||||
|
{ID: 1, Name: "c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := slice.UniqueByField(users, "ID")
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [{1 a} {2 b}]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -2577,7 +2843,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 (
|
||||||
@@ -2587,13 +2853,100 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
strs := []string{"a", "b", "a", "c", "d", "a"}
|
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||||
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||||
|
|
||||||
fmt.Println(modifiedStrs)
|
fmt.Println(modifiedStrs)
|
||||||
fmt.Println(count)
|
fmt.Println(count)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [ 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:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/yLYcBTyeQIz)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
even := func(n int) bool { return n%2 == 0 }
|
||||||
|
|
||||||
|
resultEven, resultAfterFirstEven := slice.Break(nums, even)
|
||||||
|
|
||||||
|
fmt.Println(resultEven)
|
||||||
|
fmt.Println(resultAfterFirstEven)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1]
|
||||||
|
// [2 3 4 5]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<span id="c">RightPadding</span>
|
||||||
|
|
||||||
|
<p>RightPadding adds padding to the right end of a slice.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/0_2rlLEMBXL)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
padded := slice.RightPadding(nums, 0, 3)
|
||||||
|
fmt.Println(padded)
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5 0 0 0]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<span id="LeftPadding">LeftPadding</span>
|
||||||
|
|
||||||
|
<p>LeftPadding adds padding to the left begin of a slice.</p>
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
|
||||||
|
```
|
||||||
|
|
||||||
|
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jlQVoelLl2k)</span></b>
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
padded := slice.LeftPadding(nums, 0, 3)
|
||||||
|
fmt.Println(padded)
|
||||||
|
// Output:
|
||||||
|
// [0 0 0 1 2 3 4 5]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -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
|
||||||
|
}
|
||||||
```
|
```
|
||||||
@@ -33,7 +33,7 @@ features:
|
|||||||
details: Well structure, test for every exported function.
|
details: Well structure, test for every exported function.
|
||||||
---
|
---
|
||||||
|
|
||||||
<p style="position:relative; top: -316px;left: 560px;">
|
<p style="position:relative; inline-block;top: -330px;left: 30%">
|
||||||
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
|
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
|
||||||
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
|
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ features:
|
|||||||
details: 结构良好,测试每个导出的函数。
|
details: 结构良好,测试每个导出的函数。
|
||||||
---
|
---
|
||||||
|
|
||||||
<p style="position:relative;display: inline-block;top: -316px;left: 32%">
|
<p style="position:relative;display: inline-block;top: -330px;left: 30%">
|
||||||
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
|
<img style="display: inline-block;margin-right:10px;" src="https://img.shields.io/github/stars/duke-git/lancet?style=social" alt="">
|
||||||
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
|
<img style="display: inline-block" src="https://img.shields.io/github/forks/duke-git/lancet?style=social" alt="">
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
1171
docs/package-lock.json
generated
1171
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,6 @@
|
|||||||
"docs:preview": "vitepress preview"
|
"docs:preview": "vitepress preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitepress": "^1.0.0-rc.4"
|
"vitepress": "^1.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,58 +119,43 @@ func CreateDir(absPath string) error {
|
|||||||
// if dstPath exists, it will return an error.
|
// if dstPath exists, it will return an error.
|
||||||
// Play: https://go.dev/play/p/YAyFTA_UuPb
|
// Play: https://go.dev/play/p/YAyFTA_UuPb
|
||||||
func CopyDir(srcPath string, dstPath string) error {
|
func CopyDir(srcPath string, dstPath string) error {
|
||||||
if !IsDir(srcPath) {
|
srcInfo, err := os.Stat(srcPath)
|
||||||
return errors.New("source path is not a directory")
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
srcPath, err = filepath.Abs(srcPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to get source directory info: %w", err)
|
||||||
}
|
|
||||||
if IsExist(dstPath) {
|
|
||||||
return errors.New("destination path already exists")
|
|
||||||
}
|
|
||||||
dstPath, err = filepath.Abs(dstPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get srcPath's file info
|
if !srcInfo.IsDir() {
|
||||||
srcFileInfo, err := os.Stat(srcPath)
|
return fmt.Errorf("source path is not a directory: %s", srcPath)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create dstPath with srcPath's mode
|
err = os.MkdirAll(dstPath, 0755)
|
||||||
err = os.MkdirAll(dstPath, srcFileInfo.Mode())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to create destination directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error {
|
entries, err := os.ReadDir(srcPath)
|
||||||
if srcPath == path {
|
if err != nil {
|
||||||
return nil
|
return fmt.Errorf("failed to read source directory: %w", err)
|
||||||
}
|
}
|
||||||
curDstPath := filepath.Join(dstPath, filepath.Base(path))
|
|
||||||
if info.IsDir() {
|
for _, entry := range entries {
|
||||||
err = CopyDir(path, curDstPath)
|
srcDir := filepath.Join(srcPath, entry.Name())
|
||||||
|
dstDir := filepath.Join(dstPath, entry.Name())
|
||||||
|
|
||||||
|
if entry.IsDir() {
|
||||||
|
err := CopyDir(srcDir, dstDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = CopyFile(path, curDstPath)
|
err := CopyFile(srcDir, dstDir)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = os.Chmod(curDstPath, info.Mode())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
}
|
||||||
})
|
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDir checks if the path is directory or not.
|
// IsDir checks if the path is directory or not.
|
||||||
@@ -869,7 +854,7 @@ func isCsvSupportedType(v interface{}) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ChunkRead reads a block from the file at the specified offset and returns all lines within the block
|
// ChunkRead reads a block from the file at the specified offset and returns all lines within the block
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/r0hPmKWhsgf
|
||||||
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error) {
|
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 := file.ReadAt(buf, offset) // 从指定偏移读取数据到缓冲区
|
n, err := file.ReadAt(buf, offset) // 从指定偏移读取数据到缓冲区
|
||||||
@@ -901,7 +886,7 @@ func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]str
|
|||||||
// chunkSizeMB 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整
|
// chunkSizeMB 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整
|
||||||
// maxGoroutine 并发读取分块的数量,设置为0时使用CPU核心数
|
// maxGoroutine 并发读取分块的数量,设置为0时使用CPU核心数
|
||||||
// linesCh用于接收返回结果的通道。
|
// linesCh用于接收返回结果的通道。
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/teMXnCsdSEw
|
||||||
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error {
|
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error {
|
||||||
if chunkSizeMB == 0 {
|
if chunkSizeMB == 0 {
|
||||||
chunkSizeMB = 100
|
chunkSizeMB = 100
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/v2/mathutil"
|
||||||
|
"github.com/duke-git/lancet/v2/strutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -15,63 +18,63 @@ import (
|
|||||||
// http://en.wikipedia.org/wiki/Binary_prefix
|
// http://en.wikipedia.org/wiki/Binary_prefix
|
||||||
const (
|
const (
|
||||||
// Decimal
|
// Decimal
|
||||||
UnitB = 1
|
unitB = 1
|
||||||
UnitKB = 1000
|
unitKB = 1000
|
||||||
UnitMB = 1000 * UnitKB
|
unitMB = 1000 * unitKB
|
||||||
UnitGB = 1000 * UnitMB
|
unitGB = 1000 * unitMB
|
||||||
UnitTB = 1000 * UnitGB
|
unitTB = 1000 * unitGB
|
||||||
UnitPB = 1000 * UnitTB
|
unitPB = 1000 * unitTB
|
||||||
UnitEB = 1000 * UnitPB
|
unitEB = 1000 * unitPB
|
||||||
|
|
||||||
// Binary
|
// Binary
|
||||||
UnitBiB = 1
|
unitBiB = 1
|
||||||
UnitKiB = 1024
|
unitKiB = 1024
|
||||||
UnitMiB = 1024 * UnitKiB
|
unitMiB = 1024 * unitKiB
|
||||||
UnitGiB = 1024 * UnitMiB
|
unitGiB = 1024 * unitMiB
|
||||||
UnitTiB = 1024 * UnitGiB
|
unitTiB = 1024 * unitGiB
|
||||||
UnitPiB = 1024 * UnitTiB
|
unitPiB = 1024 * unitTiB
|
||||||
UnitEiB = 1024 * UnitPiB
|
unitEiB = 1024 * unitPiB
|
||||||
)
|
)
|
||||||
|
|
||||||
// type byteUnitMap map[byte]int64
|
// type byteUnitMap map[byte]int64
|
||||||
|
|
||||||
var (
|
var (
|
||||||
decimalByteMap = map[string]uint64{
|
decimalByteMap = map[string]uint64{
|
||||||
"b": UnitB,
|
"b": unitB,
|
||||||
"kb": UnitKB,
|
"kb": unitKB,
|
||||||
"mb": UnitMB,
|
"mb": unitMB,
|
||||||
"gb": UnitGB,
|
"gb": unitGB,
|
||||||
"tb": UnitTB,
|
"tb": unitTB,
|
||||||
"pb": UnitPB,
|
"pb": unitPB,
|
||||||
"eb": UnitEB,
|
"eb": unitEB,
|
||||||
|
|
||||||
// Without suffix
|
// Without suffix
|
||||||
"": UnitB,
|
"": unitB,
|
||||||
"k": UnitKB,
|
"k": unitKB,
|
||||||
"m": UnitMB,
|
"m": unitMB,
|
||||||
"g": UnitGB,
|
"g": unitGB,
|
||||||
"t": UnitTB,
|
"t": unitTB,
|
||||||
"p": UnitPB,
|
"p": unitPB,
|
||||||
"e": UnitEB,
|
"e": unitEB,
|
||||||
}
|
}
|
||||||
|
|
||||||
binaryByteMap = map[string]uint64{
|
binaryByteMap = map[string]uint64{
|
||||||
"bi": UnitBiB,
|
"bi": unitBiB,
|
||||||
"kib": UnitKiB,
|
"kib": unitKiB,
|
||||||
"mib": UnitMiB,
|
"mib": unitMiB,
|
||||||
"gib": UnitGiB,
|
"gib": unitGiB,
|
||||||
"tib": UnitTiB,
|
"tib": unitTiB,
|
||||||
"pib": UnitPiB,
|
"pib": unitPiB,
|
||||||
"eib": UnitEiB,
|
"eib": unitEiB,
|
||||||
|
|
||||||
// Without suffix
|
// Without suffix
|
||||||
"": UnitBiB,
|
"": unitBiB,
|
||||||
"ki": UnitKiB,
|
"ki": unitKiB,
|
||||||
"mi": UnitMiB,
|
"mi": unitMiB,
|
||||||
"gi": UnitGiB,
|
"gi": unitGiB,
|
||||||
"ti": UnitTiB,
|
"ti": unitTiB,
|
||||||
"pi": UnitPiB,
|
"pi": unitPiB,
|
||||||
"ei": UnitEiB,
|
"ei": unitEiB,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -84,26 +87,28 @@ var (
|
|||||||
// The precision parameter specifies the number of digits after the decimal point, which defaults to 4.
|
// The precision parameter specifies the number of digits after the decimal point, which defaults to 4.
|
||||||
// Play: https://go.dev/play/p/FPXs1suwRcs
|
// Play: https://go.dev/play/p/FPXs1suwRcs
|
||||||
func DecimalBytes(size float64, precision ...int) string {
|
func DecimalBytes(size float64, precision ...int) string {
|
||||||
p := 5
|
pointPosition := 4
|
||||||
if len(precision) > 0 {
|
if len(precision) > 0 {
|
||||||
p = precision[0] + 1
|
pointPosition = precision[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
size, unit := calculateByteSize(size, 1000.0, decimalByteUnits)
|
size, unit := calculateByteSize(size, 1000.0, decimalByteUnits)
|
||||||
|
|
||||||
return fmt.Sprintf("%.*g%s", p, size, unit)
|
return roundToToString(size, pointPosition) + unit
|
||||||
}
|
}
|
||||||
|
|
||||||
// BinaryBytes returns a human-readable byte size under binary standard (base 1024)
|
// BinaryBytes returns a human-readable byte size under binary standard (base 1024)
|
||||||
// The precision parameter specifies the number of digits after the decimal point, which defaults to 4.
|
// The precision parameter specifies the number of digits after the decimal point, which defaults to 4.
|
||||||
// Play: https://go.dev/play/p/G9oHHMCAZxP
|
// Play: https://go.dev/play/p/G9oHHMCAZxP
|
||||||
func BinaryBytes(size float64, precision ...int) string {
|
func BinaryBytes(size float64, precision ...int) string {
|
||||||
p := 5
|
pointPosition := 4
|
||||||
if len(precision) > 0 {
|
if len(precision) > 0 {
|
||||||
p = precision[0] + 1
|
pointPosition = precision[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
size, unit := calculateByteSize(size, 1024.0, binaryByteUnits)
|
size, unit := calculateByteSize(size, 1024.0, binaryByteUnits)
|
||||||
|
|
||||||
return fmt.Sprintf("%.*g%s", p, size, unit)
|
return roundToToString(size, pointPosition) + unit
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateByteSize(size float64, base float64, byteUnits []string) (float64, string) {
|
func calculateByteSize(size float64, base float64, byteUnits []string) (float64, string) {
|
||||||
@@ -116,6 +121,29 @@ func calculateByteSize(size float64, base float64, byteUnits []string) (float64,
|
|||||||
return size, byteUnits[i]
|
return size, byteUnits[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func roundToToString(x float64, max ...int) string {
|
||||||
|
pointPosition := 4
|
||||||
|
if len(max) > 0 {
|
||||||
|
pointPosition = max[0]
|
||||||
|
}
|
||||||
|
result := mathutil.RoundToString(x, pointPosition)
|
||||||
|
|
||||||
|
// 删除小数位结尾的0
|
||||||
|
decimal := strings.TrimRight(strutil.After(result, "."), "0")
|
||||||
|
if decimal == "" || pointPosition == 0 {
|
||||||
|
// 没有小数位直接返回整数
|
||||||
|
return strutil.Before(result, ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 小数位大于想要设置的位数,按需要截断
|
||||||
|
if len(decimal) > pointPosition {
|
||||||
|
return strutil.Before(result, ".") + "." + decimal[:pointPosition]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 小数位小于等于想要的位数,直接拼接返回
|
||||||
|
return strutil.Before(result, ".") + "." + decimal
|
||||||
|
}
|
||||||
|
|
||||||
// ParseDecimalBytes return the human readable bytes size string into the amount it represents(base 1000).
|
// ParseDecimalBytes return the human readable bytes size string into the amount it represents(base 1000).
|
||||||
// ParseDecimalBytes("42 MB") -> 42000000, nil
|
// ParseDecimalBytes("42 MB") -> 42000000, nil
|
||||||
// Play: https://go.dev/play/p/Am98ybWjvjj
|
// Play: https://go.dev/play/p/Am98ybWjvjj
|
||||||
|
|||||||
@@ -15,11 +15,19 @@ func TestDecimalBytes(t *testing.T) {
|
|||||||
assert.Equal("1.024KB", DecimalBytes(1024))
|
assert.Equal("1.024KB", DecimalBytes(1024))
|
||||||
assert.Equal("1.2346MB", DecimalBytes(1234567))
|
assert.Equal("1.2346MB", DecimalBytes(1234567))
|
||||||
assert.Equal("1.235MB", DecimalBytes(1234567, 3))
|
assert.Equal("1.235MB", DecimalBytes(1234567, 3))
|
||||||
assert.Equal("1.123GB", DecimalBytes(float64(1.123*UnitGB)))
|
assert.Equal("1.123GB", DecimalBytes(float64(1.123*unitGB)))
|
||||||
assert.Equal("2.123TB", DecimalBytes(float64(2.123*UnitTB)))
|
assert.Equal("2.123TB", DecimalBytes(float64(2.123*unitTB)))
|
||||||
assert.Equal("3.123PB", DecimalBytes(float64(3.123*UnitPB)))
|
assert.Equal("3.123PB", DecimalBytes(float64(3.123*unitPB)))
|
||||||
assert.Equal("4.123EB", DecimalBytes(float64(4.123*UnitEB)))
|
assert.Equal("4.123EB", DecimalBytes(float64(4.123*unitEB)))
|
||||||
assert.Equal("1EB", DecimalBytes(float64(1000*UnitPB)))
|
assert.Equal("1EB", DecimalBytes(float64(1000*unitPB)))
|
||||||
|
assert.Equal("62MB", DecimalBytes(61812496, 0))
|
||||||
|
assert.Equal("61.8MB", DecimalBytes(61812496, 1))
|
||||||
|
assert.Equal("401MB", DecimalBytes(401000000))
|
||||||
|
assert.Equal("401MB", DecimalBytes(401000000, 0))
|
||||||
|
assert.Equal("4MB", DecimalBytes(4010000, 0))
|
||||||
|
|
||||||
|
assert.Equal("4MB", DecimalBytes(4000000, 0))
|
||||||
|
assert.Equal("4MB", DecimalBytes(4000000, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBinaryBytes(t *testing.T) {
|
func TestBinaryBytes(t *testing.T) {
|
||||||
@@ -31,6 +39,10 @@ func TestBinaryBytes(t *testing.T) {
|
|||||||
assert.Equal("1MiB", BinaryBytes(1024*1024))
|
assert.Equal("1MiB", BinaryBytes(1024*1024))
|
||||||
assert.Equal("1.1774MiB", BinaryBytes(1234567))
|
assert.Equal("1.1774MiB", BinaryBytes(1234567))
|
||||||
assert.Equal("1.18MiB", BinaryBytes(1234567, 2))
|
assert.Equal("1.18MiB", BinaryBytes(1234567, 2))
|
||||||
|
|
||||||
|
assert.Equal("10KiB", BinaryBytes(10240, 0))
|
||||||
|
assert.Equal("10KiB", BinaryBytes(10240, 1))
|
||||||
|
assert.Equal("10KiB", BinaryBytes(10240, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseDecimalBytes(t *testing.T) {
|
func TestParseDecimalBytes(t *testing.T) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package function
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -84,36 +85,97 @@ func Delay(delay time.Duration, fn any, args ...any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
|
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
|
||||||
|
// Deprecated: Use Debounce function instead.
|
||||||
// Play: https://go.dev/play/p/absuEGB_GN7
|
// Play: https://go.dev/play/p/absuEGB_GN7
|
||||||
func Debounced(fn func(), duration time.Duration) func() {
|
func Debounced(fn func(), delay time.Duration) func() {
|
||||||
// Catch programming error while constructing the closure
|
debouncedFn, _ := Debounce(fn, delay)
|
||||||
mustBeFunction(fn)
|
return debouncedFn
|
||||||
|
}
|
||||||
|
|
||||||
timer := time.NewTimer(duration)
|
// Debounce creates a debounced version of the provided function.
|
||||||
timer.Stop()
|
// Play: todo
|
||||||
|
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func()) {
|
||||||
|
var (
|
||||||
|
timer *time.Timer
|
||||||
|
mu sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
go func() {
|
debouncedFn = func() {
|
||||||
for {
|
mu.Lock()
|
||||||
<-timer.C
|
defer mu.Unlock()
|
||||||
go fn()
|
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
return func() { timer.Reset(duration) }
|
timer = time.AfterFunc(delay, func() {
|
||||||
|
fn()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelFn = func() {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return debouncedFn, cancelFn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throttle creates a throttled version of the provided function.
|
||||||
|
// The returned function guarantees that it will only be invoked at most once per interval.
|
||||||
|
// Play: todo
|
||||||
|
func Throttle(fn func(), interval time.Duration) func() {
|
||||||
|
var (
|
||||||
|
timer *time.Timer
|
||||||
|
lastRun time.Time
|
||||||
|
mu sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
if now.Sub(lastRun) >= interval {
|
||||||
|
fn()
|
||||||
|
lastRun = now
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
|
timer = nil
|
||||||
|
}
|
||||||
|
} else if timer == nil {
|
||||||
|
delay := interval - now.Sub(lastRun)
|
||||||
|
|
||||||
|
timer = time.AfterFunc(delay, func() {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
fn()
|
||||||
|
lastRun = time.Now()
|
||||||
|
timer = nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule invoke function every duration time, util close the returned bool channel.
|
// Schedule invoke function every duration time, util close the returned bool channel.
|
||||||
// Play: https://go.dev/play/p/hbON-Xeyn5N
|
// Play: https://go.dev/play/p/hbON-Xeyn5N
|
||||||
func Schedule(d time.Duration, fn any, args ...any) chan bool {
|
func Schedule(duration time.Duration, fn any, args ...any) chan bool {
|
||||||
// Catch programming error while constructing the closure
|
// Catch programming error while constructing the closure
|
||||||
mustBeFunction(fn)
|
mustBeFunction(fn)
|
||||||
|
|
||||||
quit := make(chan bool)
|
quit := make(chan bool)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
unsafeInvokeFunc(fn, args...)
|
unsafeInvokeFunc(fn, args...)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-time.After(d):
|
case <-time.After(duration):
|
||||||
case <-quit:
|
case <-quit:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -139,6 +201,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")
|
||||||
|
|||||||
@@ -79,6 +79,32 @@ func ExampleDelay() {
|
|||||||
// hello
|
// hello
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleDebounce() {
|
||||||
|
callCount := 0
|
||||||
|
fn := func() {
|
||||||
|
callCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
debouncedFn, _ := Debounce(fn, 500*time.Millisecond)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
debouncedFn()
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
fmt.Println(callCount)
|
||||||
|
|
||||||
|
debouncedFn()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
fmt.Println(callCount)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 1
|
||||||
|
// 2
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleDebounced() {
|
func ExampleDebounced() {
|
||||||
count := 0
|
count := 0
|
||||||
add := func() {
|
add := func() {
|
||||||
@@ -174,3 +200,24 @@ func ExampleAcceptIf() {
|
|||||||
// 0
|
// 0
|
||||||
// false
|
// false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleThrottle() {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
fn := func() {
|
||||||
|
callCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
throttledFn := Throttle(fn, 1*time.Second)
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
throttledFn()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
fmt.Println(callCount)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 1
|
||||||
|
}
|
||||||
|
|||||||
@@ -125,24 +125,178 @@ func TestDebounced(t *testing.T) {
|
|||||||
assert.Equal(2, count)
|
assert.Equal(2, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDebounce(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestDebounce")
|
||||||
|
|
||||||
|
t.Run("Single call", func(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
debouncedFn, _ := Debounce(func() {
|
||||||
|
callCount++
|
||||||
|
}, 500*time.Millisecond)
|
||||||
|
|
||||||
|
debouncedFn()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
assert.Equal(1, callCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Multiple calls within debounce interval", func(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
debouncedFn, _ := Debounce(func() {
|
||||||
|
callCount++
|
||||||
|
}, 1*time.Second)
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
go func(index int) {
|
||||||
|
time.Sleep(time.Duration(index) * 200 * time.Millisecond)
|
||||||
|
debouncedFn()
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
assert.Equal(1, callCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Immediate consecutive calls", func(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
debouncedFn, _ := Debounce(func() {
|
||||||
|
callCount++
|
||||||
|
}, 500*time.Millisecond)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
debouncedFn()
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
assert.Equal(1, callCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Cancel calls", func(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
debouncedFn, cancelFn := Debounce(func() {
|
||||||
|
callCount++
|
||||||
|
}, 500*time.Millisecond)
|
||||||
|
|
||||||
|
debouncedFn()
|
||||||
|
|
||||||
|
cancelFn()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
assert.Equal(0, callCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestThrottle(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestThrottle")
|
||||||
|
|
||||||
|
t.Run("Single call", func(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
throttledFn := Throttle(func() {
|
||||||
|
callCount++
|
||||||
|
}, 1*time.Second)
|
||||||
|
|
||||||
|
throttledFn()
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
assert.Equal(1, callCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Multiple calls within throttle interval", func(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
throttledFn := Throttle(func() {
|
||||||
|
callCount++
|
||||||
|
}, 1*time.Second)
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
throttledFn()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
assert.Equal(1, callCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Mutiple calls space out throttle interval", func(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
throttledFn := Throttle(func() {
|
||||||
|
callCount++
|
||||||
|
}, 500*time.Millisecond)
|
||||||
|
|
||||||
|
throttledFn()
|
||||||
|
time.Sleep(600 * time.Millisecond)
|
||||||
|
|
||||||
|
throttledFn()
|
||||||
|
time.Sleep(600 * time.Millisecond)
|
||||||
|
|
||||||
|
throttledFn()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
assert.Equal(3, callCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Call function near the end of the interval", func(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
|
||||||
|
throttledFn := Throttle(func() {
|
||||||
|
callCount++
|
||||||
|
}, 1*time.Second)
|
||||||
|
|
||||||
|
throttledFn()
|
||||||
|
time.Sleep(900 * time.Millisecond)
|
||||||
|
|
||||||
|
throttledFn()
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
assert.Equal(2, callCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestSchedule(t *testing.T) {
|
func TestSchedule(t *testing.T) {
|
||||||
// assert := internal.NewAssert(t, "TestSchedule")
|
assert := internal.NewAssert(t, "TestSchedule")
|
||||||
|
|
||||||
var res []string
|
t.Run("Single call", func(t *testing.T) {
|
||||||
appendStr := func(s string) {
|
res := []string{}
|
||||||
res = append(res, s)
|
appendStr := func(s string) {
|
||||||
}
|
res = append(res, s)
|
||||||
|
}
|
||||||
|
stop := Schedule(200*time.Millisecond, appendStr, "*")
|
||||||
|
close(stop)
|
||||||
|
|
||||||
stop := Schedule(1*time.Second, appendStr, "*")
|
time.Sleep(400 * time.Millisecond)
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
close(stop)
|
|
||||||
|
|
||||||
t.Log(res)
|
assert.Equal([]string{"*"}, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Multiple calls", func(t *testing.T) {
|
||||||
|
res := []string{}
|
||||||
|
appendStr := func(s string) {
|
||||||
|
res = append(res, s)
|
||||||
|
}
|
||||||
|
stop := Schedule(200*time.Millisecond, appendStr, "*")
|
||||||
|
|
||||||
|
time.Sleep(800 * time.Millisecond)
|
||||||
|
|
||||||
|
close(stop)
|
||||||
|
|
||||||
|
assert.Equal([]string{"*", "*", "*", "*"}, res)
|
||||||
|
})
|
||||||
|
|
||||||
// todo: in github action, for now, this test is not working sometimes
|
|
||||||
// res maybe [* * * * *] or [* * * * * *]
|
|
||||||
// expected := []string{"*", "*", "*", "*", "*"}
|
|
||||||
// assert.Equal(expected, res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPipeline(t *testing.T) {
|
func TestPipeline(t *testing.T) {
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
@@ -66,7 +70,12 @@ func ValuesBy[K comparable, V any, T any](m map[K]V, mapper func(item V) T) []T
|
|||||||
// Merge maps, next key will overwrite previous key.
|
// Merge maps, next key will overwrite previous key.
|
||||||
// Play: https://go.dev/play/p/H95LENF1uB-
|
// Play: https://go.dev/play/p/H95LENF1uB-
|
||||||
func Merge[K comparable, V any](maps ...map[K]V) map[K]V {
|
func Merge[K comparable, V any](maps ...map[K]V) map[K]V {
|
||||||
result := make(map[K]V, 0)
|
size := 0
|
||||||
|
for i := range maps {
|
||||||
|
size += len(maps[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[K]V, size)
|
||||||
|
|
||||||
for _, m := range maps {
|
for _, m := range maps {
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
@@ -306,7 +315,7 @@ func HasKey[K comparable, V any](m map[K]V, key K) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MapToStruct converts map to struct
|
// MapToStruct converts map to struct
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/7wYyVfX38Dp
|
||||||
func MapToStruct(m map[string]any, structObj any) error {
|
func MapToStruct(m map[string]any, structObj any) error {
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
err := setStructField(structObj, k, v)
|
err := setStructField(structObj, k, v)
|
||||||
@@ -375,8 +384,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 +392,84 @@ 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.
|
||||||
|
// Play: https://go.dev/play/p/43gEM2po-qy
|
||||||
|
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V) {
|
||||||
|
keys := make([]K, 0, len(m))
|
||||||
|
|
||||||
|
// store the map’s keys into a slice
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the slice of keys
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
return keys[i] < keys[j]
|
||||||
|
})
|
||||||
|
|
||||||
|
// adjust the order of values according to the sorted keys
|
||||||
|
sortedValues := make([]V, len(keys))
|
||||||
|
for i, k := range keys {
|
||||||
|
sortedValues[i] = m[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys, sortedValues
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSortedSlicesWithComparator converts a map to two slices sorted by key and using a custom comparison function:
|
||||||
|
// one for the keys and another for the values.
|
||||||
|
// Play: https://go.dev/play/p/0nlPo6YLdt3
|
||||||
|
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V) {
|
||||||
|
keys := make([]K, 0, len(m))
|
||||||
|
|
||||||
|
// store the map’s keys into a slice
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the key slice using the provided comparison function
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
return comparator(keys[i], keys[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
// adjust the order of values according to the sorted keys
|
||||||
|
sortedValues := make([]V, len(keys))
|
||||||
|
for i, k := range keys {
|
||||||
|
sortedValues[i] = m[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys, sortedValues
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrSet returns value of the given key or set the given value value if not present.
|
||||||
|
// Play: https://go.dev/play/p/IVQwO1OkEJC
|
||||||
|
func GetOrSet[K comparable, V any](m map[K]V, key K, value V) V {
|
||||||
|
if v, ok := m[key]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
m[key] = value
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortByKey sorts the map by its keys and returns a new map with sorted keys.
|
||||||
|
// Play: todo
|
||||||
|
func SortByKey[K constraints.Ordered, V any](m map[K]V, less func(a, b K) bool) (sortedKeysMap map[K]V) {
|
||||||
|
keys := make([]K, 0, len(m))
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
return less(keys[i], keys[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
sortedKeysMap = make(map[K]V, len(m))
|
||||||
|
for _, k := range keys {
|
||||||
|
sortedKeysMap[k] = m[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleKeys() {
|
func ExampleKeys() {
|
||||||
@@ -450,3 +451,110 @@ func ExampleHasKey() {
|
|||||||
// true
|
// true
|
||||||
// false
|
// false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleMapToStruct() {
|
||||||
|
|
||||||
|
personReqMap := map[string]any{
|
||||||
|
"name": "Nothin",
|
||||||
|
"max_age": 35,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersonReq struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MaxAge int `json:"max_age"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
|
}
|
||||||
|
var personReq PersonReq
|
||||||
|
_ = MapToStruct(personReqMap, &personReq)
|
||||||
|
fmt.Println(personReq)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {Nothin 35 1 10}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleToSortedSlicesDefault() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, values := ToSortedSlicesDefault(m)
|
||||||
|
|
||||||
|
fmt.Println(keys)
|
||||||
|
fmt.Println(values)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2 3]
|
||||||
|
// [a b c]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleToSortedSlicesWithComparator() {
|
||||||
|
m1 := map[time.Time]string{
|
||||||
|
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
|
||||||
|
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
|
||||||
|
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
|
||||||
|
}
|
||||||
|
|
||||||
|
keys1, values1 := ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
|
||||||
|
return a.Before(b)
|
||||||
|
})
|
||||||
|
|
||||||
|
m2 := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
3: "c",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
keys2, values2 := ToSortedSlicesWithComparator(m2, func(a, b int) bool {
|
||||||
|
return a > b
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(keys1)
|
||||||
|
fmt.Println(values1)
|
||||||
|
|
||||||
|
fmt.Println(keys2)
|
||||||
|
fmt.Println(values2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
|
||||||
|
// [yesterday today tomorrow]
|
||||||
|
// [3 2 1]
|
||||||
|
// [c b a]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleGetOrSet() {
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
}
|
||||||
|
|
||||||
|
result1 := GetOrSet(m, 1, "1")
|
||||||
|
result2 := GetOrSet(m, 2, "b")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// a
|
||||||
|
// b
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleSortByKey() {
|
||||||
|
m := map[int]string{
|
||||||
|
3: "c",
|
||||||
|
1: "a",
|
||||||
|
4: "d",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
|
||||||
|
result := SortByKey(m, func(a, b int) bool {
|
||||||
|
return a < b
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// map[1:a 2:b 3:c 4:d]
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
@@ -105,14 +107,14 @@ func TestMerge(t *testing.T) {
|
|||||||
2: "b",
|
2: "b",
|
||||||
}
|
}
|
||||||
m2 := map[int]string{
|
m2 := map[int]string{
|
||||||
1: "1",
|
2: "c",
|
||||||
3: "2",
|
3: "d",
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := map[int]string{
|
expected := map[int]string{
|
||||||
1: "1",
|
1: "a",
|
||||||
2: "b",
|
2: "c",
|
||||||
3: "2",
|
3: "d",
|
||||||
}
|
}
|
||||||
acturl := Merge(m1, m2)
|
acturl := Merge(m1, m2)
|
||||||
|
|
||||||
@@ -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,241 @@ 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOrSet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestGetOrSet")
|
||||||
|
|
||||||
|
m := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
}
|
||||||
|
|
||||||
|
result1 := GetOrSet(m, 1, "1")
|
||||||
|
result2 := GetOrSet(m, 2, "b")
|
||||||
|
|
||||||
|
assert.Equal("a", result1)
|
||||||
|
assert.Equal("b", result2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortByKey(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestSortByKey")
|
||||||
|
|
||||||
|
m1 := map[int]string{
|
||||||
|
3: "c",
|
||||||
|
1: "a",
|
||||||
|
4: "d",
|
||||||
|
2: "b",
|
||||||
|
}
|
||||||
|
expected1 := map[int]string{
|
||||||
|
1: "a",
|
||||||
|
2: "b",
|
||||||
|
3: "c",
|
||||||
|
4: "d",
|
||||||
|
}
|
||||||
|
|
||||||
|
result1 := SortByKey(m1, func(a, b int) bool {
|
||||||
|
return a < b
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(expected1, result1)
|
||||||
|
|
||||||
|
m2 := map[string]int{
|
||||||
|
"c": 3,
|
||||||
|
"a": 1,
|
||||||
|
"d": 4,
|
||||||
|
"b": 2,
|
||||||
|
}
|
||||||
|
expected2 := map[string]int{
|
||||||
|
"d": 4,
|
||||||
|
"c": 3,
|
||||||
|
"b": 2,
|
||||||
|
"a": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
result2 := SortByKey(m2, func(a, b string) bool {
|
||||||
|
return a > b
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(expected2, result2)
|
||||||
|
}
|
||||||
|
|||||||
431
maputil/orderedmap.go
Normal file
431
maputil/orderedmap.go
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
package maputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OrderedMap is a map that maintains the order of keys.
|
||||||
|
type OrderedMap[K comparable, V any] struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
|
||||||
|
data map[K]V
|
||||||
|
order *list.List
|
||||||
|
index map[K]*list.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOrderedMap creates a new OrderedMap.
|
||||||
|
func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V] {
|
||||||
|
return &OrderedMap[K, V]{
|
||||||
|
data: make(map[K]V),
|
||||||
|
order: list.New(),
|
||||||
|
index: make(map[K]*list.Element),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value for the given key.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Set(key K, value V) {
|
||||||
|
om.mu.Lock()
|
||||||
|
defer om.mu.Unlock()
|
||||||
|
|
||||||
|
if elem, ok := om.index[key]; ok {
|
||||||
|
elem.Value = value
|
||||||
|
om.order.MoveToBack(elem)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
om.data[key] = value
|
||||||
|
|
||||||
|
elem := om.order.PushBack(key)
|
||||||
|
om.index[key] = elem
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value for the given key.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Get(key K) (V, bool) {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
|
||||||
|
value, ok := om.data[key]
|
||||||
|
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the given key.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Delete(key K) {
|
||||||
|
om.mu.Lock()
|
||||||
|
defer om.mu.Unlock()
|
||||||
|
|
||||||
|
if elem, ok := om.index[key]; ok {
|
||||||
|
om.order.Remove(elem)
|
||||||
|
delete(om.data, key)
|
||||||
|
delete(om.index, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear clears the map.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Clear() {
|
||||||
|
om.mu.Lock()
|
||||||
|
defer om.mu.Unlock()
|
||||||
|
|
||||||
|
om.data = make(map[K]V)
|
||||||
|
om.order.Init()
|
||||||
|
om.index = make(map[K]*list.Element)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Front returns the first key-value pair.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Front() (struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}, bool) {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
|
||||||
|
if elem := om.order.Front(); elem != nil {
|
||||||
|
key := elem.Value.(K)
|
||||||
|
value := om.data[key]
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back returns the last key-value pair.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Back() (struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}, bool) {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
|
||||||
|
if elem := om.order.Back(); elem != nil {
|
||||||
|
key := elem.Value.(K)
|
||||||
|
value := om.data[key]
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range calls the given function for each key-value pair.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Range(iteratee func(key K, value V) bool) {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
|
||||||
|
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
|
||||||
|
key := elem.Value.(K)
|
||||||
|
value := om.data[key]
|
||||||
|
|
||||||
|
if !iteratee(key, value) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns the keys in order.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Keys() []K {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
|
||||||
|
keys := make([]K, 0, len(om.data))
|
||||||
|
|
||||||
|
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
|
||||||
|
keys = append(keys, elem.Value.(K))
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns the values in order.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Values() []V {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
|
||||||
|
values := make([]V, 0, len(om.data))
|
||||||
|
|
||||||
|
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
|
||||||
|
key := elem.Value.(K)
|
||||||
|
values = append(values, om.data[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of key-value pairs.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Len() int {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
|
||||||
|
return len(om.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains returns true if the given key exists.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Contains(key K) bool {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
|
||||||
|
_, ok := om.data[key]
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elements returns the key-value pairs in order.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Elements() []struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
} {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
|
||||||
|
elements := make([]struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}, 0, len(om.data))
|
||||||
|
|
||||||
|
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
|
||||||
|
key := elem.Value.(K)
|
||||||
|
value := om.data[key]
|
||||||
|
elements = append(elements, struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}{Key: key, Value: value})
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iter returns a channel that yields key-value pairs in order.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) Iter() <-chan struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
} {
|
||||||
|
ch := make(chan struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
defer close(ch)
|
||||||
|
|
||||||
|
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
|
||||||
|
key := elem.Value.(K)
|
||||||
|
value := om.data[key]
|
||||||
|
|
||||||
|
ch <- struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}{Key: key, Value: value}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReverseIter returns a channel that yields key-value pairs in reverse order.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) ReverseIter() <-chan struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
} {
|
||||||
|
ch := make(chan struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
defer close(ch)
|
||||||
|
|
||||||
|
for elem := om.order.Back(); elem != nil; elem = elem.Prev() {
|
||||||
|
key := elem.Value.(K)
|
||||||
|
value := om.data[key]
|
||||||
|
|
||||||
|
ch <- struct {
|
||||||
|
Key K
|
||||||
|
Value V
|
||||||
|
}{Key: key, Value: value}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortByValue sorts the map by key given less function.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) SortByKey(less func(a, b K) bool) {
|
||||||
|
om.mu.Lock()
|
||||||
|
defer om.mu.Unlock()
|
||||||
|
|
||||||
|
keys := make([]K, 0, om.order.Len())
|
||||||
|
for elem := om.order.Front(); elem != nil; elem = elem.Next() {
|
||||||
|
keys = append(keys, elem.Value.(K))
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
return less(keys[i], keys[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
om.order.Init()
|
||||||
|
om.index = make(map[K]*list.Element)
|
||||||
|
for _, key := range keys {
|
||||||
|
elem := om.order.PushBack(key)
|
||||||
|
om.index[key] = elem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) {
|
||||||
|
om.mu.RLock()
|
||||||
|
defer om.mu.RUnlock()
|
||||||
|
|
||||||
|
tempMap := make(map[string]V)
|
||||||
|
for e := om.order.Front(); e != nil; e = e.Next() {
|
||||||
|
key := e.Value.(K)
|
||||||
|
keyStr, err := keyToString(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tempMap[keyStr] = om.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(tempMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
// Play: todo
|
||||||
|
func (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error {
|
||||||
|
om.mu.Lock()
|
||||||
|
defer om.mu.Unlock()
|
||||||
|
|
||||||
|
tempMap := make(map[string]V)
|
||||||
|
if err := json.Unmarshal(data, &tempMap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
om.data = make(map[K]V)
|
||||||
|
om.order.Init()
|
||||||
|
om.index = make(map[K]*list.Element)
|
||||||
|
|
||||||
|
for keyStr, value := range tempMap {
|
||||||
|
key, err := stringToKey[K](keyStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
om.data[key] = value
|
||||||
|
elem := om.order.PushBack(key)
|
||||||
|
om.index[key] = elem
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyToString[K any](key K) (string, error) {
|
||||||
|
switch v := any(key).(type) {
|
||||||
|
case int:
|
||||||
|
return strconv.Itoa(v), nil
|
||||||
|
case float64:
|
||||||
|
return strconv.FormatFloat(v, 'f', -1, 64), nil
|
||||||
|
case string:
|
||||||
|
return v, nil
|
||||||
|
default:
|
||||||
|
// 使用反射将未知类型转换为字符串
|
||||||
|
rv := reflect.ValueOf(key)
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return strconv.FormatInt(rv.Int(), 10), nil
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
return strconv.FormatUint(rv.Uint(), 10), nil
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return strconv.FormatFloat(rv.Float(), 'f', -1, 64), nil
|
||||||
|
case reflect.String:
|
||||||
|
return rv.String(), nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported key type: %T", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringToKey[K any](s string) (K, error) {
|
||||||
|
var zero K
|
||||||
|
switch any(zero).(type) {
|
||||||
|
case int:
|
||||||
|
value, err := strconv.Atoi(s)
|
||||||
|
return any(value).(K), err
|
||||||
|
case float64:
|
||||||
|
value, err := strconv.ParseFloat(s, 64)
|
||||||
|
return any(value).(K), err
|
||||||
|
case string:
|
||||||
|
return any(s).(K), nil
|
||||||
|
default:
|
||||||
|
// 使用反射恢复未知类型的键
|
||||||
|
rv := reflect.ValueOf(&zero).Elem()
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
val, err := strconv.ParseInt(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
rv.SetInt(val)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
val, err := strconv.ParseUint(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
rv.SetUint(val)
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
val, err := strconv.ParseFloat(s, 64)
|
||||||
|
if err != nil {
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
rv.SetFloat(val)
|
||||||
|
case reflect.String:
|
||||||
|
rv.SetString(s)
|
||||||
|
default:
|
||||||
|
return zero, fmt.Errorf("unsupported key type: %T", zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv.Interface().(K), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
245
maputil/orderedmap_test.go
Normal file
245
maputil/orderedmap_test.go
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
package maputil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOrderedMap_Set_Get(t *testing.T) {
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("a", 1)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_Set_Get")
|
||||||
|
|
||||||
|
val, ok := om.Get("a")
|
||||||
|
assert.Equal(1, val)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
val, ok = om.Get("d")
|
||||||
|
assert.Equal(false, ok)
|
||||||
|
assert.Equal(0, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_Front_Back(t *testing.T) {
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("a", 1)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_Front_Back")
|
||||||
|
|
||||||
|
frontElement, ok := om.Front()
|
||||||
|
assert.Equal("a", frontElement.Key)
|
||||||
|
assert.Equal(1, frontElement.Value)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
|
||||||
|
backElement, ok := om.Back()
|
||||||
|
assert.Equal("c", backElement.Key)
|
||||||
|
assert.Equal(3, backElement.Value)
|
||||||
|
assert.Equal(true, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_Delete_Clear(t *testing.T) {
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("a", 1)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_Delete_Clear")
|
||||||
|
|
||||||
|
assert.Equal(3, om.Len())
|
||||||
|
|
||||||
|
om.Delete("b")
|
||||||
|
assert.Equal(2, om.Len())
|
||||||
|
|
||||||
|
om.Clear()
|
||||||
|
assert.Equal(0, om.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_Keys_Values(t *testing.T) {
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("a", 1)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_Keys_Values")
|
||||||
|
|
||||||
|
assert.Equal([]string{"a", "b", "c"}, om.Keys())
|
||||||
|
assert.Equal([]int{1, 2, 3}, om.Values())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_Contains(t *testing.T) {
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("a", 1)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_Contains")
|
||||||
|
|
||||||
|
assert.Equal(true, om.Contains("a"))
|
||||||
|
assert.Equal(false, om.Contains("d"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_Eelements(t *testing.T) {
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("a", 1)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_Eelements")
|
||||||
|
|
||||||
|
elements := []struct {
|
||||||
|
Key string
|
||||||
|
Value int
|
||||||
|
}{
|
||||||
|
{"a", 1},
|
||||||
|
{"b", 2},
|
||||||
|
{"c", 3},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(elements, om.Elements())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_Range(t *testing.T) {
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("a", 1)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
om.Set("d", 4)
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_Range")
|
||||||
|
|
||||||
|
var keys []string
|
||||||
|
om.Range(func(key string, value int) bool {
|
||||||
|
keys = append(keys, key)
|
||||||
|
return key != "c"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal([]string{"a", "b", "c"}, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_Iter(t *testing.T) {
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("a", 1)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_Iter")
|
||||||
|
|
||||||
|
var items []struct {
|
||||||
|
Key string
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
iterCh := om.Iter()
|
||||||
|
|
||||||
|
for item := range iterCh {
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []struct {
|
||||||
|
Key string
|
||||||
|
Value int
|
||||||
|
}{
|
||||||
|
{"a", 1},
|
||||||
|
{"b", 2},
|
||||||
|
{"c", 3},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(expected, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_ReverseIter(t *testing.T) {
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("a", 1)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_ReverseIter")
|
||||||
|
|
||||||
|
var items []struct {
|
||||||
|
Key string
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
iterCh := om.ReverseIter()
|
||||||
|
|
||||||
|
for item := range iterCh {
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []struct {
|
||||||
|
Key string
|
||||||
|
Value int
|
||||||
|
}{
|
||||||
|
{"c", 3},
|
||||||
|
{"b", 2},
|
||||||
|
{"a", 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(expected, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_SortByKey(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_SortByKey")
|
||||||
|
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("d", 4)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
om.Set("a", 1)
|
||||||
|
|
||||||
|
om.SortByKey(func(a, b string) bool {
|
||||||
|
return a < b
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal([]string{"a", "b", "c", "d"}, om.Keys())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_MarshalJSON(t *testing.T) {
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
|
||||||
|
om.Set("a", 1)
|
||||||
|
om.Set("b", 2)
|
||||||
|
om.Set("c", 3)
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_MarshalJSON")
|
||||||
|
|
||||||
|
jsonBytes, err := om.MarshalJSON()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("MarshalJSON error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(`{"a":1,"b":2,"c":3}`, string(jsonBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedMap_UnmarshalJSON(t *testing.T) {
|
||||||
|
assert := internal.NewAssert(t, "TestOrderedMap_UnmarshalJSON")
|
||||||
|
|
||||||
|
jsonStr := `{"a":1,"b":2,"c":3}`
|
||||||
|
|
||||||
|
om := NewOrderedMap[string, int]()
|
||||||
|
err := om.UnmarshalJSON([]byte(jsonStr))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("MarshalJSON error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(3, om.Len())
|
||||||
|
assert.Equal(true, om.Contains("a"))
|
||||||
|
assert.Equal(true, om.Contains("b"))
|
||||||
|
assert.Equal(true, om.Contains("c"))
|
||||||
|
}
|
||||||
@@ -101,7 +101,7 @@ func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FloorToFloat round down to n decimal places.
|
// FloorToFloat round down to n decimal places.
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/vbCBrQHZEED
|
||||||
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
||||||
tmp := math.Pow(10.0, float64(n))
|
tmp := math.Pow(10.0, float64(n))
|
||||||
x *= T(tmp)
|
x *= T(tmp)
|
||||||
@@ -110,7 +110,7 @@ func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FloorToString round down to n decimal places.
|
// FloorToString round down to n decimal places.
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/Qk9KPd2IdDb
|
||||||
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
||||||
tmp := math.Pow(10.0, float64(n))
|
tmp := math.Pow(10.0, float64(n))
|
||||||
x *= T(tmp)
|
x *= T(tmp)
|
||||||
@@ -120,7 +120,7 @@ func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CeilToFloat round up to n decimal places.
|
// CeilToFloat round up to n decimal places.
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/8hOeSADZPCo
|
||||||
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
||||||
tmp := math.Pow(10.0, float64(n))
|
tmp := math.Pow(10.0, float64(n))
|
||||||
x *= T(tmp)
|
x *= T(tmp)
|
||||||
@@ -129,7 +129,7 @@ func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CeilToString round up to n decimal places.
|
// CeilToString round up to n decimal places.
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/wy5bYEyUKKG
|
||||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
||||||
tmp := math.Pow(10.0, float64(n))
|
tmp := math.Pow(10.0, float64(n))
|
||||||
x *= T(tmp)
|
x *= T(tmp)
|
||||||
@@ -391,7 +391,7 @@ func Abs[T constraints.Integer | constraints.Float](x T) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Div returns the result of x divided by y.
|
// Div returns the result of x divided by y.
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/WLxDdGXXYat
|
||||||
func Div[T constraints.Float | constraints.Integer](x T, y T) float64 {
|
func Div[T constraints.Float | constraints.Integer](x T, y T) float64 {
|
||||||
return float64(x) / float64(y)
|
return float64(x) / float64(y)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/convertor"
|
|
||||||
"github.com/duke-git/lancet/v2/slice"
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -103,19 +102,22 @@ type HttpRequest struct {
|
|||||||
|
|
||||||
// HttpClientConfig contains some configurations for http client
|
// HttpClientConfig contains some configurations for http client
|
||||||
type HttpClientConfig struct {
|
type HttpClientConfig struct {
|
||||||
|
Timeout time.Duration
|
||||||
SSLEnabled bool
|
SSLEnabled bool
|
||||||
TLSConfig *tls.Config
|
TLSConfig *tls.Config
|
||||||
Compressed bool
|
Compressed bool
|
||||||
HandshakeTimeout time.Duration
|
HandshakeTimeout time.Duration
|
||||||
ResponseTimeout time.Duration
|
ResponseTimeout time.Duration
|
||||||
Verbose bool
|
Verbose bool
|
||||||
|
Proxy *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultHttpClientConfig defalut client config.
|
// defaultHttpClientConfig defalut client config.
|
||||||
var defaultHttpClientConfig = &HttpClientConfig{
|
var defaultHttpClientConfig = &HttpClientConfig{
|
||||||
|
Timeout: 50 * time.Second,
|
||||||
Compressed: false,
|
Compressed: false,
|
||||||
HandshakeTimeout: 20 * time.Second,
|
HandshakeTimeout: 10 * time.Second,
|
||||||
ResponseTimeout: 40 * time.Second,
|
ResponseTimeout: 10 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
// HttpClient is used for sending http request.
|
// HttpClient is used for sending http request.
|
||||||
@@ -131,6 +133,7 @@ type HttpClient struct {
|
|||||||
func NewHttpClient() *HttpClient {
|
func NewHttpClient() *HttpClient {
|
||||||
client := &HttpClient{
|
client := &HttpClient{
|
||||||
Client: &http.Client{
|
Client: &http.Client{
|
||||||
|
Timeout: defaultHttpClientConfig.Timeout,
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSHandshakeTimeout: defaultHttpClientConfig.HandshakeTimeout,
|
TLSHandshakeTimeout: defaultHttpClientConfig.HandshakeTimeout,
|
||||||
ResponseHeaderTimeout: defaultHttpClientConfig.ResponseTimeout,
|
ResponseHeaderTimeout: defaultHttpClientConfig.ResponseTimeout,
|
||||||
@@ -164,6 +167,11 @@ func NewHttpClientWithConfig(config *HttpClientConfig) *HttpClient {
|
|||||||
client.TLS = config.TLSConfig
|
client.TLS = config.TLSConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.Proxy != nil {
|
||||||
|
transport := client.Client.Transport.(*http.Transport)
|
||||||
|
transport.Proxy = http.ProxyURL(config.Proxy)
|
||||||
|
}
|
||||||
|
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,11 +371,20 @@ func validateRequest(req *HttpRequest) error {
|
|||||||
// Play: https://go.dev/play/p/pFqMkM40w9z
|
// Play: https://go.dev/play/p/pFqMkM40w9z
|
||||||
func StructToUrlValues(targetStruct any) (url.Values, error) {
|
func StructToUrlValues(targetStruct any) (url.Values, error) {
|
||||||
result := url.Values{}
|
result := url.Values{}
|
||||||
s, err := convertor.StructToMap(targetStruct)
|
|
||||||
|
var m map[string]interface{}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(targetStruct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for k, v := range s {
|
|
||||||
|
err = json.Unmarshal(jsonBytes, &m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range m {
|
||||||
result.Add(k, fmt.Sprintf("%v", v))
|
result.Add(k, fmt.Sprintf("%v", v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
)
|
)
|
||||||
@@ -23,7 +23,8 @@ func TestHttpGet(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := HttpGet(url, header)
|
resp, err := HttpGet(url, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: " + err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
@@ -44,8 +45,10 @@ func TestHttpPost(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := HttpPost(url, header, nil, bodyParams)
|
resp, err := HttpPost(url, header, nil, bodyParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: " + err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
t.Log("response: ", resp.StatusCode, string(body))
|
t.Log("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
@@ -54,21 +57,18 @@ func TestHttpPostFormData(t *testing.T) {
|
|||||||
apiUrl := "https://jsonplaceholder.typicode.com/todos"
|
apiUrl := "https://jsonplaceholder.typicode.com/todos"
|
||||||
header := map[string]string{
|
header := map[string]string{
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
// "Content-Type": "multipart/form-data",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
postData := url.Values{}
|
postData := url.Values{}
|
||||||
postData.Add("userId", "1")
|
postData.Add("userId", "1")
|
||||||
postData.Add("title", "TestToDo")
|
postData.Add("title", "TestToDo")
|
||||||
|
|
||||||
// postData := make(map[string]string)
|
|
||||||
// postData["userId"] = "1"
|
|
||||||
// postData["title"] = "title"
|
|
||||||
|
|
||||||
resp, err := HttpPost(apiUrl, header, nil, postData)
|
resp, err := HttpPost(apiUrl, header, nil, postData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: " + err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
t.Log("response: ", resp.StatusCode, string(body))
|
t.Log("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
@@ -88,8 +88,10 @@ func TestHttpPut(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := HttpPut(url, header, nil, bodyParams)
|
resp, err := HttpPut(url, header, nil, bodyParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
t.Log("response: ", resp.StatusCode, string(body))
|
t.Log("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
@@ -109,8 +111,10 @@ func TestHttpPatch(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := HttpPatch(url, header, nil, bodyParams)
|
resp, err := HttpPatch(url, header, nil, bodyParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
t.Log("response: ", resp.StatusCode, string(body))
|
t.Log("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
@@ -119,8 +123,10 @@ func TestHttpDelete(t *testing.T) {
|
|||||||
url := "https://jsonplaceholder.typicode.com/todos/1"
|
url := "https://jsonplaceholder.typicode.com/todos/1"
|
||||||
resp, err := HttpDelete(url)
|
resp, err := HttpDelete(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
t.Log("response: ", resp.StatusCode, string(body))
|
t.Log("response: ", resp.StatusCode, string(body))
|
||||||
}
|
}
|
||||||
@@ -147,7 +153,8 @@ func TestParseResponse(t *testing.T) {
|
|||||||
|
|
||||||
resp, err := HttpGet(url, header)
|
resp, err := HttpGet(url, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type Todo struct {
|
type Todo struct {
|
||||||
@@ -160,8 +167,10 @@ func TestParseResponse(t *testing.T) {
|
|||||||
toDoResp := &Todo{}
|
toDoResp := &Todo{}
|
||||||
err = ParseHttpResponse(resp, toDoResp)
|
err = ParseHttpResponse(resp, toDoResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("response: ", toDoResp)
|
t.Log("response: ", toDoResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +187,8 @@ func TestHttpClient_Get(t *testing.T) {
|
|||||||
httpClient := NewHttpClient()
|
httpClient := NewHttpClient()
|
||||||
resp, err := httpClient.SendRequest(request)
|
resp, err := httpClient.SendRequest(request)
|
||||||
if err != nil || resp.StatusCode != 200 {
|
if err != nil || resp.StatusCode != 200 {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type Todo struct {
|
type Todo struct {
|
||||||
@@ -215,7 +225,8 @@ func TestHttpClent_Post(t *testing.T) {
|
|||||||
httpClient := NewHttpClient()
|
httpClient := NewHttpClient()
|
||||||
resp, err := httpClient.SendRequest(request)
|
resp, err := httpClient.SendRequest(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
@@ -227,16 +238,25 @@ func TestStructToUrlValues(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestStructToUrlValues")
|
assert := internal.NewAssert(t, "TestStructToUrlValues")
|
||||||
|
|
||||||
|
type CommReq struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
type TodoQuery struct {
|
type TodoQuery struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
UserId int `json:"userId"`
|
UserId int `json:"userId"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
CommReq `json:",inline"`
|
||||||
}
|
}
|
||||||
item1 := TodoQuery{
|
item1 := TodoQuery{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
UserId: 123,
|
UserId: 123,
|
||||||
Name: "",
|
Name: "",
|
||||||
|
CommReq: CommReq{
|
||||||
|
Version: "1.0",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
todoValues, err := StructToUrlValues(item1)
|
todoValues, err := StructToUrlValues(item1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("params is invalid: %v", err)
|
t.Errorf("params is invalid: %v", err)
|
||||||
@@ -245,19 +265,10 @@ func TestStructToUrlValues(t *testing.T) {
|
|||||||
assert.Equal("1", todoValues.Get("id"))
|
assert.Equal("1", todoValues.Get("id"))
|
||||||
assert.Equal("123", todoValues.Get("userId"))
|
assert.Equal("123", todoValues.Get("userId"))
|
||||||
assert.Equal("", todoValues.Get("name"))
|
assert.Equal("", todoValues.Get("name"))
|
||||||
|
assert.Equal("1.0", todoValues.Get("version"))
|
||||||
item2 := TodoQuery{
|
|
||||||
Id: 2,
|
|
||||||
UserId: 456,
|
|
||||||
}
|
|
||||||
queryValues2, _ := StructToUrlValues(item2)
|
|
||||||
|
|
||||||
assert.Equal("2", queryValues2.Get("id"))
|
|
||||||
assert.Equal("456", queryValues2.Get("userId"))
|
|
||||||
assert.Equal("", queryValues2.Get("name"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleFileRequest(t *testing.T, w http.ResponseWriter, r *http.Request) {
|
func handleFileRequest(t *testing.T, _ http.ResponseWriter, r *http.Request) {
|
||||||
err := r.ParseMultipartForm(1024)
|
err := r.ParseMultipartForm(1024)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -361,3 +372,25 @@ func TestSendRequestWithFilePath(t *testing.T) {
|
|||||||
t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode)
|
t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProxy(t *testing.T) {
|
||||||
|
config := &HttpClientConfig{
|
||||||
|
HandshakeTimeout: 20 * time.Second,
|
||||||
|
ResponseTimeout: 40 * time.Second,
|
||||||
|
// Use the proxy ip to add it here
|
||||||
|
//Proxy: &url.URL{
|
||||||
|
// Scheme: "http",
|
||||||
|
// Host: "46.17.63.166:18888",
|
||||||
|
//},
|
||||||
|
}
|
||||||
|
httpClient := NewHttpClientWithConfig(config)
|
||||||
|
resp, err := httpClient.Get("https://www.ipplus360.com/getLocation")
|
||||||
|
if err != nil {
|
||||||
|
t.Log("net error: ", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,17 +11,24 @@ import (
|
|||||||
// Of returns a pointer to the value `v`.
|
// Of returns a pointer to the value `v`.
|
||||||
// Play: https://go.dev/play/p/HFd70x4DrMj
|
// Play: https://go.dev/play/p/HFd70x4DrMj
|
||||||
func Of[T any](v T) *T {
|
func Of[T any](v T) *T {
|
||||||
|
if IsNil(v) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unwrap returns the value from the pointer.
|
// Unwrap returns the value from the pointer.
|
||||||
// Play: https://go.dev/play/p/cgeu3g7cjWb
|
//
|
||||||
|
// Play: https://go.dev/play/p/cgeu3g7cjWb
|
||||||
|
// Deprecated: Please use UnwrapOr
|
||||||
func Unwrap[T any](p *T) T {
|
func Unwrap[T any](p *T) T {
|
||||||
return *p
|
return *p
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnwarpOr returns the value from the pointer or fallback if the pointer is nil.
|
// UnwarpOr returns the value from the pointer or fallback if the pointer is nil.
|
||||||
// Play: https://go.dev/play/p/mmNaLC38W8C
|
//
|
||||||
|
// Play: https://go.dev/play/p/mmNaLC38W8C
|
||||||
|
// Deprecated: Please use UnwrapOr
|
||||||
func UnwarpOr[T any](p *T, fallback T) T {
|
func UnwarpOr[T any](p *T, fallback T) T {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return fallback
|
return fallback
|
||||||
@@ -30,7 +37,9 @@ func UnwarpOr[T any](p *T, fallback T) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
|
// UnwarpOrDefault returns the value from the pointer or the default value if the pointer is nil.
|
||||||
// Play: https://go.dev/play/p/ZnGIHf8_o4E
|
//
|
||||||
|
// Play: https://go.dev/play/p/ZnGIHf8_o4E
|
||||||
|
// Deprecated: Please use UnwrapOr
|
||||||
func UnwarpOrDefault[T any](p *T) T {
|
func UnwarpOrDefault[T any](p *T) T {
|
||||||
var v T
|
var v T
|
||||||
|
|
||||||
@@ -40,9 +49,24 @@ func UnwarpOrDefault[T any](p *T) T {
|
|||||||
return *p
|
return *p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnwrapOr returns the value from the pointer or fallback if the pointer is nil.
|
||||||
|
func UnwrapOr[T any](p *T, fallback ...T) T {
|
||||||
|
if !IsNil(p) {
|
||||||
|
return *p
|
||||||
|
}
|
||||||
|
if len(fallback) > 0 {
|
||||||
|
return fallback[0]
|
||||||
|
}
|
||||||
|
var t T
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
// ExtractPointer returns the underlying value by the given interface type
|
// ExtractPointer returns the underlying value by the given interface type
|
||||||
// Play: https://go.dev/play/p/D7HFjeWU2ZP
|
// Play: https://go.dev/play/p/D7HFjeWU2ZP
|
||||||
func ExtractPointer(value any) any {
|
func ExtractPointer(value any) any {
|
||||||
|
if IsNil(value) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
t := reflect.TypeOf(value)
|
t := reflect.TypeOf(value)
|
||||||
v := reflect.ValueOf(value)
|
v := reflect.ValueOf(value)
|
||||||
|
|
||||||
@@ -56,3 +80,8 @@ func ExtractPointer(value any) any {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNil returns true if the given interface is nil or the underlying value is nil.
|
||||||
|
func IsNil(i interface{}) bool {
|
||||||
|
return i == nil || (reflect.ValueOf(i).Kind() == reflect.Ptr && reflect.ValueOf(i).IsNil())
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,3 +90,61 @@ func ExampleExtractPointer() {
|
|||||||
// Output:
|
// Output:
|
||||||
// 1
|
// 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleIsNil() {
|
||||||
|
a := 1
|
||||||
|
b := &a
|
||||||
|
c := &b
|
||||||
|
d := &c
|
||||||
|
e := &d
|
||||||
|
var f *int
|
||||||
|
|
||||||
|
result1 := IsNil(a)
|
||||||
|
result2 := IsNil(b)
|
||||||
|
result3 := IsNil(c)
|
||||||
|
result4 := IsNil(d)
|
||||||
|
result5 := IsNil(e)
|
||||||
|
result6 := IsNil(f)
|
||||||
|
result7 := IsNil(nil)
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
fmt.Println(result5)
|
||||||
|
fmt.Println(result6)
|
||||||
|
fmt.Println(result7)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// false
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleUnwrapOr() {
|
||||||
|
a := 123
|
||||||
|
b := "abc"
|
||||||
|
|
||||||
|
var c *int
|
||||||
|
var d *string
|
||||||
|
|
||||||
|
result1 := UnwrapOr(&a, 456)
|
||||||
|
result2 := UnwrapOr(&b, "efg")
|
||||||
|
result3 := UnwrapOr(c, 456)
|
||||||
|
result4 := UnwrapOr(d, "def")
|
||||||
|
|
||||||
|
fmt.Println(result1)
|
||||||
|
fmt.Println(result2)
|
||||||
|
fmt.Println(result3)
|
||||||
|
fmt.Println(result4)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 123
|
||||||
|
// abc
|
||||||
|
// 456
|
||||||
|
// def
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ func TestUnwarpOr(t *testing.T) {
|
|||||||
assert.Equal("def", UnwarpOr(d, "def"))
|
assert.Equal("def", UnwarpOr(d, "def"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnwarpOrDefault(t *testing.T) {
|
func TestUnwrapOrDefault(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
assert := internal.NewAssert(t, "TestUnwarpOrDefault")
|
assert := internal.NewAssert(t, "TestUnwrapOrDefault")
|
||||||
|
|
||||||
a := 123
|
a := 123
|
||||||
b := "abc"
|
b := "abc"
|
||||||
|
|||||||
187
random/random.go
187
random/random.go
@@ -23,6 +23,7 @@ const (
|
|||||||
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
||||||
|
AllChars = Numeral + LowwerLetters + UpperLetters + SymbolChars
|
||||||
)
|
)
|
||||||
|
|
||||||
var rn = rand.NewSource(time.Now().UnixNano())
|
var rn = rand.NewSource(time.Now().UnixNano())
|
||||||
@@ -31,6 +32,27 @@ func init() {
|
|||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RandBool generates a random boolean value (true or false).
|
||||||
|
// Play: todo
|
||||||
|
func RandBool() bool {
|
||||||
|
return rand.Intn(2) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandBoolSlice generates a random boolean slice of specified length.
|
||||||
|
// Play: todo
|
||||||
|
func RandBoolSlice(length int) []bool {
|
||||||
|
if length <= 0 {
|
||||||
|
return []bool{}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]bool, length)
|
||||||
|
for i := range result {
|
||||||
|
result[i] = RandBool()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// RandInt generate random int between [min, max).
|
// RandInt generate random int between [min, max).
|
||||||
// Play: https://go.dev/play/p/pXyyAAI5YxD
|
// Play: https://go.dev/play/p/pXyyAAI5YxD
|
||||||
func RandInt(min, max int) int {
|
func RandInt(min, max int) int {
|
||||||
@@ -42,9 +64,54 @@ func RandInt(min, max int) int {
|
|||||||
min, max = max, min
|
min, max = max, min
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if min == 0 && max == math.MaxInt {
|
||||||
|
return rand.Int()
|
||||||
|
}
|
||||||
|
|
||||||
return rand.Intn(max-min) + min
|
return rand.Intn(max-min) + min
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RandIntSlice generates a slice of random integers.
|
||||||
|
// The generated integers are between min and max (exclusive).
|
||||||
|
// Play: todo
|
||||||
|
func RandIntSlice(length, min, max int) []int {
|
||||||
|
if length <= 0 || min > max {
|
||||||
|
return []int{}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]int, length)
|
||||||
|
for i := range result {
|
||||||
|
result[i] = RandInt(min, max)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandUniqueIntSlice generate a slice of random int of length that do not repeat.
|
||||||
|
// Play: https://go.dev/play/p/uBkRSOz73Ec
|
||||||
|
func RandUniqueIntSlice(length, min, max int) []int {
|
||||||
|
if min > max {
|
||||||
|
return []int{}
|
||||||
|
}
|
||||||
|
if length > max-min {
|
||||||
|
length = max - min
|
||||||
|
}
|
||||||
|
|
||||||
|
nums := make([]int, length)
|
||||||
|
used := make(map[int]struct{}, length)
|
||||||
|
for i := 0; i < length; {
|
||||||
|
r := RandInt(min, max)
|
||||||
|
if _, use := used[r]; use {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
used[r] = struct{}{}
|
||||||
|
nums[i] = r
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return nums
|
||||||
|
}
|
||||||
|
|
||||||
// RandFloat generate random float64 number between [min, max) with specific precision.
|
// RandFloat generate random float64 number between [min, max) with specific precision.
|
||||||
// Play: https://go.dev/play/p/zbD_tuobJtr
|
// Play: https://go.dev/play/p/zbD_tuobJtr
|
||||||
func RandFloat(min, max float64, precision int) float64 {
|
func RandFloat(min, max float64, precision int) float64 {
|
||||||
@@ -61,6 +128,24 @@ func RandFloat(min, max float64, precision int) float64 {
|
|||||||
return mathutil.RoundToFloat(n, precision)
|
return mathutil.RoundToFloat(n, precision)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RandFloats generate a slice of random float64 numbers of length that do not repeat.
|
||||||
|
// Play: https://go.dev/play/p/I3yndUQ-rhh
|
||||||
|
func RandFloats(length int, min, max float64, precision int) []float64 {
|
||||||
|
nums := make([]float64, length)
|
||||||
|
used := make(map[float64]struct{}, length)
|
||||||
|
for i := 0; i < length; {
|
||||||
|
r := RandFloat(min, max, precision)
|
||||||
|
if _, use := used[r]; use {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
used[r] = struct{}{}
|
||||||
|
nums[i] = r
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return nums
|
||||||
|
}
|
||||||
|
|
||||||
// RandBytes generate random byte slice.
|
// RandBytes generate random byte slice.
|
||||||
// Play: https://go.dev/play/p/EkiLESeXf8d
|
// Play: https://go.dev/play/p/EkiLESeXf8d
|
||||||
func RandBytes(length int) []byte {
|
func RandBytes(length int) []byte {
|
||||||
@@ -76,12 +161,69 @@ func RandBytes(length int) []byte {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandString generate random alpha string of specified length.
|
// RandString generate random alphabeta string of specified length.
|
||||||
// Play: https://go.dev/play/p/W2xvRUXA7Mi
|
// Play: https://go.dev/play/p/W2xvRUXA7Mi
|
||||||
func RandString(length int) string {
|
func RandString(length int) string {
|
||||||
return random(Letters, length)
|
return random(Letters, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RandString generate a slice of random string of length strLen based on charset.
|
||||||
|
// chartset should be one of the following: random.Numeral, random.LowwerLetters, random.UpperLetters
|
||||||
|
// random.Letters, random.SymbolChars, random.AllChars. or a combination of them.
|
||||||
|
// Play: todo
|
||||||
|
func RandStringSlice(charset string, sliceLen, strLen int) []string {
|
||||||
|
if sliceLen <= 0 || strLen <= 0 {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]string, sliceLen)
|
||||||
|
|
||||||
|
for i := range result {
|
||||||
|
result[i] = random(charset, strLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandFromGivenSlice generate a random element from given slice.
|
||||||
|
// Play: todo
|
||||||
|
func RandFromGivenSlice[T any](slice []T) T {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
var zero T
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
return slice[rand.Intn(len(slice))]
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandSliceFromGivenSlice generate a random slice of length num from given slice.
|
||||||
|
// - If repeatable is true, the generated slice may contain duplicate elements.
|
||||||
|
//
|
||||||
|
// Play: todo
|
||||||
|
func RandSliceFromGivenSlice[T any](slice []T, num int, repeatable bool) []T {
|
||||||
|
if num <= 0 || len(slice) == 0 {
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
if !repeatable && num > len(slice) {
|
||||||
|
num = len(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]T, num)
|
||||||
|
if repeatable {
|
||||||
|
for i := range result {
|
||||||
|
result[i] = slice[rand.Intn(len(slice))]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shuffled := make([]T, len(slice))
|
||||||
|
copy(shuffled, slice)
|
||||||
|
rand.Shuffle(len(shuffled), func(i, j int) {
|
||||||
|
shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
|
||||||
|
})
|
||||||
|
result = shuffled[:num]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// RandUpper generate a random upper case string of specified length.
|
// RandUpper generate a random upper case string of specified length.
|
||||||
// Play: https://go.dev/play/p/29QfOh0DVuh
|
// Play: https://go.dev/play/p/29QfOh0DVuh
|
||||||
func RandUpper(length int) string {
|
func RandUpper(length int) string {
|
||||||
@@ -185,46 +327,3 @@ func UUIdV4() (string, error) {
|
|||||||
|
|
||||||
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
|
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandUniqueIntSlice generate a slice of random int of length n that do not repeat.
|
|
||||||
// Play: https://go.dev/play/p/uBkRSOz73Ec
|
|
||||||
func RandUniqueIntSlice(n, min, max int) []int {
|
|
||||||
if min > max {
|
|
||||||
return []int{}
|
|
||||||
}
|
|
||||||
if n > max-min {
|
|
||||||
n = max - min
|
|
||||||
}
|
|
||||||
|
|
||||||
nums := make([]int, n)
|
|
||||||
used := make(map[int]struct{}, n)
|
|
||||||
for i := 0; i < n; {
|
|
||||||
r := RandInt(min, max)
|
|
||||||
if _, use := used[r]; use {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
used[r] = struct{}{}
|
|
||||||
nums[i] = r
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
return nums
|
|
||||||
}
|
|
||||||
|
|
||||||
// RandFloats generate a slice of random float64 numbers of length n that do not repeat.
|
|
||||||
// Play: https://go.dev/play/p/I3yndUQ-rhh
|
|
||||||
func RandFloats(n int, min, max float64, precision int) []float64 {
|
|
||||||
nums := make([]float64, n)
|
|
||||||
used := make(map[float64]struct{}, n)
|
|
||||||
for i := 0; i < n; {
|
|
||||||
r := RandFloat(min, max, precision)
|
|
||||||
if _, use := used[r]; use {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
used[r] = struct{}{}
|
|
||||||
nums[i] = r
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
return nums
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -43,6 +43,21 @@ func ExampleRandString() {
|
|||||||
// 6
|
// 6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleRandFromGivenSlice() {
|
||||||
|
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon",
|
||||||
|
"mango", "nectarine", "orange"}
|
||||||
|
|
||||||
|
result := RandFromGivenSlice(goods)
|
||||||
|
fmt.Println(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleRandSliceFromGivenSlice() {
|
||||||
|
goods := []string{"apple", "banana", "cherry", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon",
|
||||||
|
"mango", "nectarine", "orange"}
|
||||||
|
chosen3goods := RandSliceFromGivenSlice(goods, 3, false)
|
||||||
|
fmt.Println(chosen3goods)
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleRandUpper() {
|
func ExampleRandUpper() {
|
||||||
pattern := `^[A-Z]+$`
|
pattern := `^[A-Z]+$`
|
||||||
reg := regexp.MustCompile(pattern)
|
reg := regexp.MustCompile(pattern)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
|
"github.com/duke-git/lancet/v2/validator"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRandString(t *testing.T) {
|
func TestRandString(t *testing.T) {
|
||||||
@@ -196,3 +197,167 @@ func TestRandFloats(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(len(numbers), 5)
|
assert.Equal(len(numbers), 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRandIntSlice(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestRandIntSlice")
|
||||||
|
|
||||||
|
t.Run("empty slice", func(t *testing.T) {
|
||||||
|
numbers := RandIntSlice(-1, 1, 5)
|
||||||
|
assert.Equal([]int{}, numbers)
|
||||||
|
|
||||||
|
numbers = RandIntSlice(0, 1, 5)
|
||||||
|
assert.Equal([]int{}, numbers)
|
||||||
|
|
||||||
|
numbers = RandIntSlice(3, 5, 1)
|
||||||
|
assert.Equal([]int{}, numbers)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("random int slice", func(t *testing.T) {
|
||||||
|
numbers := RandIntSlice(5, 1, 1)
|
||||||
|
assert.Equal([]int{1, 1, 1, 1, 1}, numbers)
|
||||||
|
|
||||||
|
numbers = RandIntSlice(5, 1, 5)
|
||||||
|
assert.Equal(5, len(numbers))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandStringSlice(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestRandStringSlice")
|
||||||
|
|
||||||
|
t.Run("empty slice", func(t *testing.T) {
|
||||||
|
strs := RandStringSlice(Letters, -1, -1)
|
||||||
|
assert.Equal([]string{}, strs)
|
||||||
|
|
||||||
|
strs = RandStringSlice(Letters, 0, 0)
|
||||||
|
assert.Equal([]string{}, strs)
|
||||||
|
|
||||||
|
strs = RandStringSlice(Letters, -1, 0)
|
||||||
|
assert.Equal([]string{}, strs)
|
||||||
|
|
||||||
|
strs = RandStringSlice(Letters, 0, -1)
|
||||||
|
assert.Equal([]string{}, strs)
|
||||||
|
|
||||||
|
strs = RandStringSlice(Letters, 1, 0)
|
||||||
|
assert.Equal([]string{}, strs)
|
||||||
|
|
||||||
|
strs = RandStringSlice(Letters, 0, 1)
|
||||||
|
assert.Equal([]string{}, strs)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("random string slice", func(t *testing.T) {
|
||||||
|
strs := RandStringSlice(Letters, 4, 6)
|
||||||
|
assert.Equal(4, len(strs))
|
||||||
|
|
||||||
|
for _, s := range strs {
|
||||||
|
assert.Equal(true, validator.IsAlpha(s))
|
||||||
|
assert.Equal(6, len(s))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// fail test: chinese character is not supported for now
|
||||||
|
// t.Run("random string slice of chinese ", func(t *testing.T) {
|
||||||
|
// strs := RandStringSlice("你好你好你好你好你好你好你好你好你好", 4, 6)
|
||||||
|
// t.Log(strs)
|
||||||
|
|
||||||
|
// assert.Equal(4, len(strs))
|
||||||
|
// for _, s := range strs {
|
||||||
|
// assert.Equal(true, validator.ContainChinese(s))
|
||||||
|
// assert.Equal(6, len(s))
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandFromGivenSlice(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestRandFromGivenSlice")
|
||||||
|
|
||||||
|
randomSet := []any{"a", 8, "王", true, 1.1}
|
||||||
|
result := RandFromGivenSlice(randomSet)
|
||||||
|
find := false
|
||||||
|
for _, v := range randomSet {
|
||||||
|
if v == result {
|
||||||
|
find = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.Equal(true, find)
|
||||||
|
|
||||||
|
emptyAnyRandomSet := []any{}
|
||||||
|
emptyAnyResult := RandFromGivenSlice(emptyAnyRandomSet)
|
||||||
|
assert.IsNil(emptyAnyResult)
|
||||||
|
|
||||||
|
emptyIntRandomSet := []int{}
|
||||||
|
emtpyIntResult := RandFromGivenSlice(emptyIntRandomSet)
|
||||||
|
assert.Equal(0, emtpyIntResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandSliceFromGivenSlice(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestRandSliceFromGivenSlice")
|
||||||
|
|
||||||
|
randomSet := []any{"a", 8, "王", true, 1.1}
|
||||||
|
repeatableResult := RandSliceFromGivenSlice(randomSet, 8, true)
|
||||||
|
assert.Equal(8, len(repeatableResult))
|
||||||
|
unrepeatableResult := RandSliceFromGivenSlice(randomSet, 8, false)
|
||||||
|
assert.Equal(len(randomSet), len(unrepeatableResult))
|
||||||
|
|
||||||
|
var findCount int
|
||||||
|
for _, v := range repeatableResult {
|
||||||
|
for _, vv := range randomSet {
|
||||||
|
if v == vv {
|
||||||
|
findCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.Equal(8, findCount)
|
||||||
|
findCount = 0
|
||||||
|
|
||||||
|
for _, v := range unrepeatableResult {
|
||||||
|
for _, vv := range randomSet {
|
||||||
|
if v == vv {
|
||||||
|
findCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.Equal(len(randomSet), findCount)
|
||||||
|
|
||||||
|
emptyAnyRandomSet := []any{}
|
||||||
|
emptyAnyResult := RandSliceFromGivenSlice(emptyAnyRandomSet, 3, true)
|
||||||
|
assert.Equal([]any{}, emptyAnyResult)
|
||||||
|
|
||||||
|
emptyIntRandomSet := []int{}
|
||||||
|
emtpyIntResult := RandSliceFromGivenSlice(emptyIntRandomSet, 3, true)
|
||||||
|
assert.Equal([]int{}, emtpyIntResult)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandBool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestRandBool")
|
||||||
|
|
||||||
|
result := RandBool()
|
||||||
|
assert.Equal(true, result == true || result == false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandBoolSlice(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := internal.NewAssert(t, "TestRandBoolSlice")
|
||||||
|
|
||||||
|
t.Run("empty slice", func(t *testing.T) {
|
||||||
|
bools := RandBoolSlice(-1)
|
||||||
|
assert.Equal([]bool{}, bools)
|
||||||
|
|
||||||
|
bools = RandBoolSlice(0)
|
||||||
|
assert.Equal([]bool{}, bools)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("random bool slice", func(t *testing.T) {
|
||||||
|
bools := RandBoolSlice(6)
|
||||||
|
assert.Equal(6, len(bools))
|
||||||
|
|
||||||
|
for _, b := range bools {
|
||||||
|
assert.Equal(true, b == true || b == false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func RetryTimes(n uint) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RetryWithCustomBackoff set abitary custom backoff strategy
|
// RetryWithCustomBackoff set abitary custom backoff strategy
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/jIm_o2vb5Y4
|
||||||
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
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/PDet2ZQZwcB
|
||||||
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
|
||||||
// Play: todo
|
// Play: https://go.dev/play/p/xp1avQmn16X
|
||||||
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")
|
||||||
|
|||||||
207
slice/slice.go
207
slice/slice.go
@@ -50,12 +50,23 @@ func ContainBy[T any](slice []T, predicate func(item T) bool) bool {
|
|||||||
// ContainSubSlice check if the slice contain a given subslice or not.
|
// ContainSubSlice check if the slice contain a given subslice or not.
|
||||||
// Play: https://go.dev/play/p/bcuQ3UT6Sev
|
// Play: https://go.dev/play/p/bcuQ3UT6Sev
|
||||||
func ContainSubSlice[T comparable](slice, subSlice []T) bool {
|
func ContainSubSlice[T comparable](slice, subSlice []T) bool {
|
||||||
for _, v := range subSlice {
|
if len(subSlice) == 0 {
|
||||||
if !Contain(slice, v) {
|
return true
|
||||||
|
}
|
||||||
|
if len(slice) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
elementMap := make(map[T]struct{}, len(slice))
|
||||||
|
for _, item := range slice {
|
||||||
|
elementMap[item] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range subSlice {
|
||||||
|
if _, ok := elementMap[item]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,26 +92,32 @@ func Chunk[T any](slice []T, size int) [][]T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey.
|
// Compact creates a slice with all falsey values removed. The values false, nil, 0, and "" are falsey.
|
||||||
// Play: https://go.dev/play/p/pO5AnxEr3TK
|
// Play: https://go.dev/play/p/pO5AnxEr3TK
|
||||||
func Compact[T comparable](slice []T) []T {
|
func Compact[T comparable](slice []T) []T {
|
||||||
var zero T
|
var zero T
|
||||||
|
|
||||||
result := []T{}
|
result := make([]T, 0, len(slice))
|
||||||
|
|
||||||
for _, v := range slice {
|
for _, v := range slice {
|
||||||
if v != zero {
|
if v != zero {
|
||||||
result = append(result, v)
|
result = append(result, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result[:len(result):len(result)]
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concat creates a new slice concatenating slice with any additional slices.
|
// Concat creates a new slice concatenating slice with any additional slices.
|
||||||
// Play: https://go.dev/play/p/gPt-q7zr5mk
|
// Play: https://go.dev/play/p/gPt-q7zr5mk
|
||||||
func Concat[T any](slice []T, slices ...[]T) []T {
|
func Concat[T any](slices ...[]T) []T {
|
||||||
result := append([]T{}, slice...)
|
totalLen := 0
|
||||||
|
for _, v := range slices {
|
||||||
|
totalLen += len(v)
|
||||||
|
if totalLen < 0 {
|
||||||
|
panic("len out of range")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result := make([]T, 0, totalLen)
|
||||||
|
|
||||||
for _, v := range slices {
|
for _, v := range slices {
|
||||||
result = append(result, v...)
|
result = append(result, v...)
|
||||||
@@ -109,7 +126,7 @@ func Concat[T any](slice []T, slices ...[]T) []T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Difference creates an slice of whose element in slice but not in comparedSlice.
|
// Difference creates a slice of whose element in slice but not in comparedSlice.
|
||||||
// Play: https://go.dev/play/p/VXvadzLzhDa
|
// Play: https://go.dev/play/p/VXvadzLzhDa
|
||||||
func Difference[T comparable](slice, comparedSlice []T) []T {
|
func Difference[T comparable](slice, comparedSlice []T) []T {
|
||||||
result := []T{}
|
result := []T{}
|
||||||
@@ -754,36 +771,105 @@ func UpdateAt[T any](slice []T, index int, value T) []T {
|
|||||||
// Unique remove duplicate elements in slice.
|
// Unique remove duplicate elements in slice.
|
||||||
// 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 := make([]T, 0, len(slice))
|
||||||
|
seen := make(map[T]struct{}, len(slice))
|
||||||
|
|
||||||
for i := 0; i < len(slice); i++ {
|
for i := range slice {
|
||||||
v := slice[i]
|
if _, ok := seen[slice[i]]; ok {
|
||||||
skip := true
|
continue
|
||||||
for j := range result {
|
}
|
||||||
if v == result[j] {
|
|
||||||
skip = false
|
seen[slice[i]] = struct{}{}
|
||||||
|
|
||||||
|
result = append(result, slice[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueBy removes duplicate elements from the input slice based on the values returned by the iteratee function.
|
||||||
|
// The function maintains the order of the elements.
|
||||||
|
// Play: todo
|
||||||
|
func UniqueBy[T any, U comparable](slice []T, iteratee func(item T) U) []T {
|
||||||
|
result := make([]T, 0, len(slice))
|
||||||
|
seen := make(map[U]struct{}, len(slice))
|
||||||
|
|
||||||
|
for i := range slice {
|
||||||
|
key := iteratee(slice[i])
|
||||||
|
if _, ok := seen[key]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
seen[key] = struct{}{}
|
||||||
|
|
||||||
|
result = append(result, slice[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueByComparator removes duplicate elements from the input slice using the provided comparator function.
|
||||||
|
// The function maintains the order of the elements.
|
||||||
|
// Play: todo
|
||||||
|
func UniqueByComparator[T comparable](slice []T, comparator func(item T, other T) bool) []T {
|
||||||
|
result := make([]T, 0, len(slice))
|
||||||
|
seen := make([]T, 0, len(slice))
|
||||||
|
|
||||||
|
for _, item := range slice {
|
||||||
|
duplicate := false
|
||||||
|
for _, seenItem := range seen {
|
||||||
|
if comparator(item, seenItem) {
|
||||||
|
duplicate = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if skip {
|
if !duplicate {
|
||||||
result = append(result, v)
|
seen = append(seen, item)
|
||||||
|
result = append(result, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// UniqueBy call iteratee func with every item of slice, then remove duplicated.
|
// UniqueByField remove duplicate elements in struct slice by struct field.
|
||||||
// Play: https://go.dev/play/p/UR323iZLDpv
|
// Play: https://go.dev/play/p/6cifcZSPIGu
|
||||||
func UniqueBy[T comparable](slice []T, iteratee func(item T) T) []T {
|
func UniqueByField[T any](slice []T, field string) ([]T, error) {
|
||||||
result := []T{}
|
seen := map[any]struct{}{}
|
||||||
|
|
||||||
for _, v := range slice {
|
var result []T
|
||||||
val := iteratee(v)
|
for _, item := range slice {
|
||||||
result = append(result, val)
|
val, err := getField(item, field)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get field %s failed: %v", field, err)
|
||||||
|
}
|
||||||
|
if _, ok := seen[val]; !ok {
|
||||||
|
seen[val] = struct{}{}
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Unique(result)
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getField[T any](item T, field string) (interface{}, error) {
|
||||||
|
v := reflect.ValueOf(item)
|
||||||
|
t := reflect.TypeOf(item)
|
||||||
|
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
return nil, fmt.Errorf("data type %T not support, shuld be struct or pointer to struct", item)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := v.FieldByName(field)
|
||||||
|
if !f.IsValid() {
|
||||||
|
return nil, fmt.Errorf("field name %s not found", field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.FieldByName(field).Interface(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Union creates a slice of unique elements, in order, from all given slices.
|
// Union creates a slice of unique elements, in order, from all given slices.
|
||||||
@@ -823,16 +909,11 @@ func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: Please use Concat() function instead.
|
||||||
// Merge all given slices into one slice.
|
// Merge all given slices into one slice.
|
||||||
// Play: https://go.dev/play/p/lbjFp784r9N
|
// Play: https://go.dev/play/p/lbjFp784r9N
|
||||||
func Merge[T any](slices ...[]T) []T {
|
func Merge[T any](slices ...[]T) []T {
|
||||||
result := make([]T, 0)
|
return Concat(slices...)
|
||||||
|
|
||||||
for _, v := range slices {
|
|
||||||
result = append(result, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intersection creates a slice of unique elements that included by all slices.
|
// Intersection creates a slice of unique elements that included by all slices.
|
||||||
@@ -1173,7 +1254,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: todo
|
// 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++ {
|
||||||
@@ -1239,6 +1320,30 @@ func Partition[T any](slice []T, predicates ...func(item T) bool) [][]T {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Breaks a list into two parts at the point where the predicate for the first time is true.
|
||||||
|
// Play: https://go.dev/play/p/yLYcBTyeQIz
|
||||||
|
func Break[T any](values []T, predicate func(T) bool) ([]T, []T) {
|
||||||
|
a := make([]T, 0)
|
||||||
|
b := make([]T, 0)
|
||||||
|
if len(values) == 0 {
|
||||||
|
return a, b
|
||||||
|
}
|
||||||
|
matched := false
|
||||||
|
for _, value := range values {
|
||||||
|
|
||||||
|
if !matched && predicate(value) {
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if matched {
|
||||||
|
b = append(b, value)
|
||||||
|
} else {
|
||||||
|
a = append(a, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a, b
|
||||||
|
}
|
||||||
|
|
||||||
// Random get a random item of slice, return idx=-1 when slice is empty
|
// Random get a random item of slice, return idx=-1 when slice is empty
|
||||||
// Play: https://go.dev/play/p/UzpGQptWppw
|
// Play: https://go.dev/play/p/UzpGQptWppw
|
||||||
func Random[T any](slice []T) (val T, idx int) {
|
func Random[T any](slice []T) (val T, idx int) {
|
||||||
@@ -1249,3 +1354,35 @@ func Random[T any](slice []T) (val T, idx int) {
|
|||||||
idx = random.RandInt(0, len(slice))
|
idx = random.RandInt(0, len(slice))
|
||||||
return slice[idx], idx
|
return slice[idx], idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RightPadding adds padding to the right end of a slice.
|
||||||
|
// Play: https://go.dev/play/p/0_2rlLEMBXL
|
||||||
|
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
||||||
|
if paddingLength == 0 {
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
for i := 0; i < paddingLength; i++ {
|
||||||
|
slice = append(slice, paddingValue)
|
||||||
|
}
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftPadding adds padding to the left begin of a slice.
|
||||||
|
// Play: https://go.dev/play/p/jlQVoelLl2k
|
||||||
|
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
||||||
|
if paddingLength == 0 {
|
||||||
|
return slice
|
||||||
|
}
|
||||||
|
|
||||||
|
paddedSlice := make([]T, len(slice)+paddingLength)
|
||||||
|
i := 0
|
||||||
|
for ; i < paddingLength; i++ {
|
||||||
|
paddedSlice[i] = paddingValue
|
||||||
|
}
|
||||||
|
for j := 0; j < len(slice); j++ {
|
||||||
|
paddedSlice[i] = slice[j]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return paddedSlice
|
||||||
|
}
|
||||||
|
|||||||
237
slice/slice_concurrent.go
Normal file
237
slice/slice_concurrent.go
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
// Copyright 2024 dudaodong@gmail.com. All rights resulterved.
|
||||||
|
// Use of this source code is governed by MIT license
|
||||||
|
|
||||||
|
package slice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ForEachConcurrent applies the iteratee function to each item in the slice concurrently.
|
||||||
|
// Play: todo
|
||||||
|
func ForEachConcurrent[T any](slice []T, iteratee func(index int, item T), numThreads int) {
|
||||||
|
sliceLen := len(slice)
|
||||||
|
if sliceLen == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if numThreads <= 0 {
|
||||||
|
numThreads = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
chunkSize := (sliceLen + numThreads - 1) / numThreads
|
||||||
|
|
||||||
|
for i := 0; i < numThreads; i++ {
|
||||||
|
start := i * chunkSize
|
||||||
|
end := start + chunkSize
|
||||||
|
|
||||||
|
if start >= sliceLen {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if end > sliceLen {
|
||||||
|
end = sliceLen
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func(start, end int) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
for j := start; j < end; j++ {
|
||||||
|
iteratee(j, slice[j])
|
||||||
|
}
|
||||||
|
}(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapConcurrent applies the iteratee function to each item in the slice concurrently.
|
||||||
|
// Play: todo
|
||||||
|
func MapConcurrent[T any, U any](slice []T, iteratee func(index int, item T) U, numThreads int) []U {
|
||||||
|
result := make([]U, len(slice))
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
workerChan := make(chan struct{}, numThreads)
|
||||||
|
|
||||||
|
for index, item := range slice {
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
workerChan <- struct{}{}
|
||||||
|
|
||||||
|
go func(i int, v T) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
result[i] = iteratee(i, v)
|
||||||
|
|
||||||
|
<-workerChan
|
||||||
|
}(index, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReduceConcurrent reduces the slice to a single value by applying the reducer function to each item in the slice concurrently.
|
||||||
|
// Play: todo
|
||||||
|
func ReduceConcurrent[T any](slice []T, initial T, reducer func(index int, item T, agg T) T, numThreads int) T {
|
||||||
|
if numThreads <= 0 {
|
||||||
|
numThreads = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
|
sliceLen := len(slice)
|
||||||
|
chunkSize := (sliceLen + numThreads - 1) / numThreads
|
||||||
|
results := make([]T, numThreads)
|
||||||
|
|
||||||
|
for i := 0; i < numThreads; i++ {
|
||||||
|
start := i * chunkSize
|
||||||
|
end := start + chunkSize
|
||||||
|
if end > sliceLen {
|
||||||
|
end = sliceLen
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func(i, start, end int) {
|
||||||
|
defer wg.Done()
|
||||||
|
tempResult := initial
|
||||||
|
for j := start; j < end; j++ {
|
||||||
|
tempResult = reducer(j, slice[j], tempResult)
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
results[i] = tempResult
|
||||||
|
mu.Unlock()
|
||||||
|
}(i, start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
result := initial
|
||||||
|
for i, r := range results {
|
||||||
|
result = reducer(i, result, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterConcurrent applies the provided filter function `predicate` to each element of the input slice concurrently.
|
||||||
|
// Play: todo
|
||||||
|
func FilterConcurrent[T any](slice []T, predicate func(index int, item T) bool, numThreads int) []T {
|
||||||
|
result := make([]T, 0)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
workerChan := make(chan struct{}, numThreads)
|
||||||
|
|
||||||
|
for index, item := range slice {
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
workerChan <- struct{}{}
|
||||||
|
|
||||||
|
go func(i int, v T) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if predicate(i, v) {
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-workerChan
|
||||||
|
}(index, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueByParallel removes duplicate elements from the slice by parallel
|
||||||
|
// The comparator function is used to compare the elements
|
||||||
|
// The numThreads parameter specifies the number of threads to use
|
||||||
|
// If numThreads is less than or equal to 0, it will be set to 1
|
||||||
|
// The comparator function should return true if the two elements are equal
|
||||||
|
// Play: todo
|
||||||
|
func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T) bool, numThreads int) []T {
|
||||||
|
if numThreads <= 0 {
|
||||||
|
numThreads = 1
|
||||||
|
} else if numThreads > len(slice) {
|
||||||
|
numThreads = len(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
maxThreads := runtime.NumCPU()
|
||||||
|
if numThreads > maxThreads {
|
||||||
|
numThreads = maxThreads
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDuplicate := func(items []T, comparator func(item T, other T) bool) []T {
|
||||||
|
var result []T
|
||||||
|
for _, item := range items {
|
||||||
|
seen := false
|
||||||
|
for _, r := range result {
|
||||||
|
if comparator(item, r) {
|
||||||
|
seen = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !seen {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkSize := (len(slice) + numThreads - 1) / numThreads
|
||||||
|
|
||||||
|
chunks := make([][]T, 0, numThreads)
|
||||||
|
for i := 0; i < len(slice); i += chunkSize {
|
||||||
|
end := i + chunkSize
|
||||||
|
if end > len(slice) {
|
||||||
|
end = len(slice)
|
||||||
|
}
|
||||||
|
chunks = append(chunks, slice[i:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
type resultChunk struct {
|
||||||
|
index int
|
||||||
|
data []T
|
||||||
|
}
|
||||||
|
resultCh := make(chan resultChunk, numThreads)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for i, chunk := range chunks {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(index int, chunk []T) {
|
||||||
|
defer wg.Done()
|
||||||
|
resultCh <- resultChunk{index, removeDuplicate(chunk, comparator)}
|
||||||
|
}(i, chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(resultCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
results := make([][]T, len(chunks))
|
||||||
|
for r := range resultCh {
|
||||||
|
results[r.index] = r.data
|
||||||
|
}
|
||||||
|
|
||||||
|
result := []T{}
|
||||||
|
seen := make(map[T]bool)
|
||||||
|
|
||||||
|
for _, chunk := range results {
|
||||||
|
for _, item := range chunk {
|
||||||
|
if !seen[item] {
|
||||||
|
seen[item] = true
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleContain() {
|
func ExampleContain() {
|
||||||
@@ -246,6 +247,21 @@ func ExampleFilter() {
|
|||||||
// [2 4]
|
// [2 4]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleFilterConcurrent() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
|
isEven := func(i, num int) bool {
|
||||||
|
return num%2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
result := FilterConcurrent(nums, isEven, 2)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [2 4]
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleCount() {
|
func ExampleCount() {
|
||||||
nums := []int{1, 2, 3, 3, 4}
|
nums := []int{1, 2, 3, 3, 4}
|
||||||
|
|
||||||
@@ -413,6 +429,23 @@ func ExampleForEach() {
|
|||||||
// [2 3 4]
|
// [2 3 4]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleForEachConcurrent() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||||
|
|
||||||
|
result := make([]int, len(nums))
|
||||||
|
|
||||||
|
addOne := func(index int, value int) {
|
||||||
|
result[index] = value + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEachConcurrent(nums, addOne, 4)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [2 3 4 5 6 7 8 9]
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleForEachWithBreak() {
|
func ExampleForEachWithBreak() {
|
||||||
numbers := []int{1, 2, 3, 4, 5}
|
numbers := []int{1, 2, 3, 4, 5}
|
||||||
|
|
||||||
@@ -494,6 +527,18 @@ func ExampleReduce() {
|
|||||||
// 6
|
// 6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleReduceConcurrent() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
|
||||||
|
return agg + item
|
||||||
|
}, 1)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// 55
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleReduceBy() {
|
func ExampleReduceBy() {
|
||||||
result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int {
|
result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int {
|
||||||
return agg + item
|
return agg + item
|
||||||
@@ -777,7 +822,46 @@ func ExampleUniqueBy() {
|
|||||||
fmt.Println(result)
|
fmt.Println(result)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [1 2 0]
|
// [1 2 3]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleUniqueByComparator() {
|
||||||
|
uniqueNums := UniqueByComparator([]int{1, 2, 3, 1, 2, 4, 5, 6, 4}, func(item int, other int) bool {
|
||||||
|
return item == other
|
||||||
|
})
|
||||||
|
|
||||||
|
caseInsensitiveStrings := UniqueByComparator([]string{"apple", "banana", "Apple", "cherry", "Banana", "date"}, func(item string, other string) bool {
|
||||||
|
return strings.ToLower(item) == strings.ToLower(other)
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(uniqueNums)
|
||||||
|
fmt.Println(caseInsensitiveStrings)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5 6]
|
||||||
|
// [apple banana cherry date]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleUniqueByField() {
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []User{
|
||||||
|
{ID: 1, Name: "a"},
|
||||||
|
{ID: 2, Name: "b"},
|
||||||
|
{ID: 1, Name: "c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := UniqueByField(users, "ID")
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [{1 a} {2 b}]
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleUnion() {
|
func ExampleUnion() {
|
||||||
@@ -1099,6 +1183,7 @@ func ExampleRandom() {
|
|||||||
if idx >= 0 && idx < len(nums) && Contain(nums, val) {
|
if idx >= 0 && idx < len(nums) && Contain(nums, val) {
|
||||||
fmt.Println("okk")
|
fmt.Println("okk")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// okk
|
// okk
|
||||||
}
|
}
|
||||||
@@ -1108,7 +1193,61 @@ func ExampleSetToDefaultIf() {
|
|||||||
modifiedStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
modifiedStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||||
fmt.Println(modifiedStrs)
|
fmt.Println(modifiedStrs)
|
||||||
fmt.Println(count)
|
fmt.Println(count)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [ 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]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleUniqueByConcurrent() {
|
||||||
|
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
|
||||||
|
comparator := func(item int, other int) bool { return item == other }
|
||||||
|
|
||||||
|
result := UniqueByConcurrent(nums, comparator, 4)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2 3 4 5 6 7]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleMapConcurrent() {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
result := MapConcurrent(nums, func(_, n int) int { return n * n }, 4)
|
||||||
|
|
||||||
|
fmt.Println(result)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 4 9 16 25 36]
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/duke-git/lancet/v2/internal"
|
"github.com/duke-git/lancet/v2/internal"
|
||||||
@@ -15,12 +16,20 @@ func TestContain(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestContain")
|
assert := internal.NewAssert(t, "TestContain")
|
||||||
|
|
||||||
assert.Equal(true, Contain([]string{"a", "b", "c"}, "a"))
|
tests := []struct {
|
||||||
assert.Equal(false, Contain([]string{"a", "b", "c"}, "d"))
|
slice []string
|
||||||
assert.Equal(true, Contain([]string{""}, ""))
|
give string
|
||||||
assert.Equal(false, Contain([]string{}, ""))
|
want bool
|
||||||
|
}{
|
||||||
|
{[]string{"a", "b", "c"}, "a", true},
|
||||||
|
{[]string{"a", "b", "c"}, "d", false},
|
||||||
|
{[]string{""}, "", true},
|
||||||
|
{[]string{}, "", false},
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal(true, Contain([]int{1, 2, 3}, 1))
|
for _, tt := range tests {
|
||||||
|
assert.Equal(tt.want, Contain(tt.slice, tt.give))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainBy(t *testing.T) {
|
func TestContainBy(t *testing.T) {
|
||||||
@@ -29,32 +38,53 @@ func TestContainBy(t *testing.T) {
|
|||||||
assert := internal.NewAssert(t, "TestContainBy")
|
assert := internal.NewAssert(t, "TestContainBy")
|
||||||
|
|
||||||
type foo struct {
|
type foo struct {
|
||||||
A string
|
a string
|
||||||
B int
|
b int
|
||||||
}
|
}
|
||||||
|
|
||||||
array1 := []foo{{A: "1", B: 1}, {A: "2", B: 2}}
|
tests := []struct {
|
||||||
result1 := ContainBy(array1, func(f foo) bool { return f.A == "1" && f.B == 1 })
|
slice []foo
|
||||||
result2 := ContainBy(array1, func(f foo) bool { return f.A == "2" && f.B == 1 })
|
predicateFn func(f foo) bool
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
[]foo{{a: "1", b: 1}, {a: "2", b: 2}},
|
||||||
|
func(f foo) bool { return f.a == "1" && f.b == 1 },
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]foo{{a: "1", b: 1}, {a: "2", b: 2}},
|
||||||
|
func(f foo) bool { return f.a == "2" && f.b == 1 },
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
array2 := []string{"a", "b", "c"}
|
for _, tt := range tests {
|
||||||
result3 := ContainBy(array2, func(t string) bool { return t == "a" })
|
assert.Equal(tt.want, ContainBy(tt.slice, tt.predicateFn))
|
||||||
result4 := ContainBy(array2, func(t string) bool { return t == "d" })
|
}
|
||||||
|
|
||||||
assert.Equal(true, result1)
|
|
||||||
assert.Equal(false, result2)
|
|
||||||
assert.Equal(true, result3)
|
|
||||||
assert.Equal(false, result4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainSubSlice(t *testing.T) {
|
func TestContainSubSlice(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
assert := internal.NewAssert(t, "TestContainSubSlice")
|
assert := internal.NewAssert(t, "TestContainSubSlice")
|
||||||
assert.Equal(true, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "a"}))
|
|
||||||
assert.Equal(false, ContainSubSlice([]string{"a", "a", "b", "c"}, []string{"a", "d"}))
|
tests := []struct {
|
||||||
assert.Equal(true, ContainSubSlice([]int{1, 2, 3}, []int{1, 2}))
|
slice []string
|
||||||
assert.Equal(false, ContainSubSlice([]int{1, 2, 3}, []int{0, 1}))
|
subSlice []string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{[]string{"a", "b", "c"}, []string{"a", "b"}, true},
|
||||||
|
{[]string{"a", "b", "c"}, []string{"a", "d"}, false},
|
||||||
|
{[]string{"a", "b", "c"}, []string{"a", "b", "c"}, true},
|
||||||
|
{[]string{"a", "b", "c"}, []string{"a", "b", "c", "d"}, false},
|
||||||
|
{[]string{"a", "b", ""}, []string{"a", ""}, true},
|
||||||
|
{[]string{""}, []string{""}, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
assert.Equal(tt.want, ContainSubSlice(tt.slice, tt.subSlice))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChunk(t *testing.T) {
|
func TestChunk(t *testing.T) {
|
||||||
@@ -62,29 +92,24 @@ func TestChunk(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestChunk")
|
assert := internal.NewAssert(t, "TestChunk")
|
||||||
|
|
||||||
arr := []string{"a", "b", "c", "d", "e"}
|
tests := []struct {
|
||||||
|
slice []string
|
||||||
|
chuanSize int
|
||||||
|
want [][]string
|
||||||
|
}{
|
||||||
|
{[]string{"a", "b", "c", "d", "e"}, -1, [][]string{}},
|
||||||
|
{[]string{"a", "b", "c", "d", "e"}, 0, [][]string{}},
|
||||||
|
{[]string{"a", "b", "c", "d", "e"}, 1, [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}},
|
||||||
|
{[]string{"a", "b", "c", "d", "e"}, 2, [][]string{{"a", "b"}, {"c", "d"}, {"e"}}},
|
||||||
|
{[]string{"a", "b", "c", "d", "e"}, 3, [][]string{{"a", "b", "c"}, {"d", "e"}}},
|
||||||
|
{[]string{"a", "b", "c", "d", "e"}, 4, [][]string{{"a", "b", "c", "d"}, {"e"}}},
|
||||||
|
{[]string{"a", "b", "c", "d", "e"}, 5, [][]string{{"a", "b", "c", "d", "e"}}},
|
||||||
|
{[]string{"a", "b", "c", "d", "e"}, 6, [][]string{{"a", "b", "c", "d", "e"}}},
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal([][]string{}, Chunk(arr, -1))
|
for _, tt := range tests {
|
||||||
|
assert.Equal(tt.want, Chunk(tt.slice, tt.chuanSize))
|
||||||
assert.Equal([][]string{}, Chunk(arr, 0))
|
}
|
||||||
|
|
||||||
r1 := [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}}
|
|
||||||
assert.Equal(r1, Chunk(arr, 1))
|
|
||||||
|
|
||||||
r2 := [][]string{{"a", "b"}, {"c", "d"}, {"e"}}
|
|
||||||
assert.Equal(r2, Chunk(arr, 2))
|
|
||||||
|
|
||||||
r3 := [][]string{{"a", "b", "c"}, {"d", "e"}}
|
|
||||||
assert.Equal(r3, Chunk(arr, 3))
|
|
||||||
|
|
||||||
r4 := [][]string{{"a", "b", "c", "d"}, {"e"}}
|
|
||||||
assert.Equal(r4, Chunk(arr, 4))
|
|
||||||
|
|
||||||
r5 := [][]string{{"a", "b", "c", "d", "e"}}
|
|
||||||
assert.Equal(r5, Chunk(arr, 5))
|
|
||||||
|
|
||||||
r6 := [][]string{{"a", "b", "c", "d", "e"}}
|
|
||||||
assert.Equal(r6, Chunk(arr, 6))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompact(t *testing.T) {
|
func TestCompact(t *testing.T) {
|
||||||
@@ -109,6 +134,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()
|
||||||
|
|
||||||
@@ -120,6 +158,7 @@ func TestEqual(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(true, Equal(slice1, slice2))
|
assert.Equal(true, Equal(slice1, slice2))
|
||||||
assert.Equal(false, Equal(slice1, slice3))
|
assert.Equal(false, Equal(slice1, slice3))
|
||||||
|
assert.Equal(false, Equal(slice2, slice3))
|
||||||
}
|
}
|
||||||
|
|
||||||
// go test -fuzz=Fuzz -fuzztime=10s .
|
// go test -fuzz=Fuzz -fuzztime=10s .
|
||||||
@@ -149,12 +188,27 @@ func TestEvery(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestEvery")
|
assert := internal.NewAssert(t, "TestEvery")
|
||||||
|
|
||||||
nums := []int{1, 2, 3, 5}
|
|
||||||
isEven := func(i, num int) bool {
|
isEven := func(i, num int) bool {
|
||||||
return num%2 == 0
|
return num%2 == 0
|
||||||
}
|
}
|
||||||
|
isOdd := func(i, num int) bool {
|
||||||
|
return num%2 == 1
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal(false, Every(nums, isEven))
|
tests := []struct {
|
||||||
|
slice []int
|
||||||
|
predicateFn func(i, num int) bool
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{[]int{1, 3, 5, 7}, isOdd, true},
|
||||||
|
{[]int{2, 4, 6, 8}, isEven, true},
|
||||||
|
{[]int{1, 2, 3, 4}, isOdd, false},
|
||||||
|
{[]int{1, 2, 3, 4}, isEven, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
assert.Equal(tt.want, Every(tt.slice, tt.predicateFn))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNone(t *testing.T) {
|
func TestNone(t *testing.T) {
|
||||||
@@ -162,12 +216,27 @@ func TestNone(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestNone")
|
assert := internal.NewAssert(t, "TestNone")
|
||||||
|
|
||||||
nums := []int{1, 2, 3, 5}
|
isEven := func(i, num int) bool {
|
||||||
check := func(i, num int) bool {
|
return num%2 == 0
|
||||||
|
}
|
||||||
|
isOdd := func(i, num int) bool {
|
||||||
return num%2 == 1
|
return num%2 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(false, None(nums, check))
|
tests := []struct {
|
||||||
|
slice []int
|
||||||
|
predicateFn func(i, num int) bool
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{[]int{1, 3, 5, 7}, isEven, true},
|
||||||
|
{[]int{2, 4, 6, 8}, isOdd, true},
|
||||||
|
{[]int{1, 2, 3, 4}, isOdd, false},
|
||||||
|
{[]int{1, 2, 3, 4}, isEven, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
assert.Equal(tt.want, None(tt.slice, tt.predicateFn))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSome(t *testing.T) {
|
func TestSome(t *testing.T) {
|
||||||
@@ -175,12 +244,27 @@ func TestSome(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestSome")
|
assert := internal.NewAssert(t, "TestSome")
|
||||||
|
|
||||||
nums := []int{1, 2, 3, 5}
|
isEven := func(i, num int) bool {
|
||||||
hasEven := func(i, num int) bool {
|
|
||||||
return num%2 == 0
|
return num%2 == 0
|
||||||
}
|
}
|
||||||
|
isOdd := func(i, num int) bool {
|
||||||
|
return num%2 == 1
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal(true, Some(nums, hasEven))
|
tests := []struct {
|
||||||
|
slice []int
|
||||||
|
predicateFn func(i, num int) bool
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{[]int{1, 3, 5, 7}, isEven, false},
|
||||||
|
{[]int{2, 4, 6, 8}, isOdd, false},
|
||||||
|
{[]int{1, 2, 3, 4}, isOdd, true},
|
||||||
|
{[]int{1, 2, 3, 4}, isEven, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
assert.Equal(tt.want, Some(tt.slice, tt.predicateFn))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilter(t *testing.T) {
|
func TestFilter(t *testing.T) {
|
||||||
@@ -188,33 +272,37 @@ func TestFilter(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestFilter")
|
assert := internal.NewAssert(t, "TestFilter")
|
||||||
|
|
||||||
nums := []int{1, 2, 3, 4, 5}
|
t.Run("filter int slice", func(t *testing.T) {
|
||||||
isEven := func(i, num int) bool {
|
nums := []int{1, 2, 3, 4, 5}
|
||||||
return num%2 == 0
|
isEven := func(i, num int) bool {
|
||||||
}
|
return num%2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal([]int{2, 4}, Filter(nums, isEven))
|
assert.Equal([]int{2, 4}, Filter(nums, isEven))
|
||||||
|
})
|
||||||
|
|
||||||
type student struct {
|
t.Run("filter struct slice", func(t *testing.T) {
|
||||||
name string
|
type student struct {
|
||||||
age int
|
name string
|
||||||
}
|
age int
|
||||||
students := []student{
|
}
|
||||||
{"a", 10},
|
students := []student{
|
||||||
{"b", 11},
|
{"a", 10},
|
||||||
{"c", 12},
|
{"b", 11},
|
||||||
{"d", 13},
|
{"c", 12},
|
||||||
{"e", 14},
|
{"d", 13},
|
||||||
}
|
{"e", 14},
|
||||||
studentsOfAageGreat12 := []student{
|
}
|
||||||
{"d", 13},
|
studentsOfAgeGreat12 := []student{
|
||||||
{"e", 14},
|
{"d", 13},
|
||||||
}
|
{"e", 14},
|
||||||
filterFunc := func(i int, s student) bool {
|
}
|
||||||
return s.age > 12
|
filterFunc := func(i int, s student) bool {
|
||||||
}
|
return s.age > 12
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal(studentsOfAageGreat12, Filter(students, filterFunc))
|
assert.Equal(studentsOfAgeGreat12, Filter(students, filterFunc))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGroupBy(t *testing.T) {
|
func TestGroupBy(t *testing.T) {
|
||||||
@@ -235,6 +323,8 @@ func TestGroupBy(t *testing.T) {
|
|||||||
func TestGroupWith(t *testing.T) {
|
func TestGroupWith(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestGroupWith")
|
||||||
|
|
||||||
nums := []float64{6.1, 4.2, 6.3}
|
nums := []float64{6.1, 4.2, 6.3}
|
||||||
floor := func(num float64) float64 {
|
floor := func(num float64) float64 {
|
||||||
return math.Floor(num)
|
return math.Floor(num)
|
||||||
@@ -243,9 +333,8 @@ func TestGroupWith(t *testing.T) {
|
|||||||
4: {4.2},
|
4: {4.2},
|
||||||
6: {6.1, 6.3},
|
6: {6.1, 6.3},
|
||||||
}
|
}
|
||||||
actual := GroupWith(nums, floor)
|
|
||||||
assert := internal.NewAssert(t, "TestGroupWith")
|
assert.Equal(expected, GroupWith(nums, floor))
|
||||||
assert.Equal(expected, actual)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCount(t *testing.T) {
|
func TestCount(t *testing.T) {
|
||||||
@@ -281,8 +370,15 @@ func TestFind(t *testing.T) {
|
|||||||
result, ok := Find(nums, even)
|
result, ok := Find(nums, even)
|
||||||
|
|
||||||
assert := internal.NewAssert(t, "TestFind")
|
assert := internal.NewAssert(t, "TestFind")
|
||||||
|
|
||||||
assert.Equal(true, ok)
|
assert.Equal(true, ok)
|
||||||
assert.Equal(2, *result)
|
assert.Equal(2, *result)
|
||||||
|
|
||||||
|
_, ok = Find(nums, func(_ int, v int) bool {
|
||||||
|
return v == 6
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(false, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindBy(t *testing.T) {
|
func TestFindBy(t *testing.T) {
|
||||||
@@ -344,19 +440,6 @@ func TestFindLast(t *testing.T) {
|
|||||||
assert.Equal(4, *result)
|
assert.Equal(4, *result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindFoundNothing(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
nums := []int{1, 1, 1, 1, 1, 1}
|
|
||||||
findFunc := func(i, num int) bool {
|
|
||||||
return num > 1
|
|
||||||
}
|
|
||||||
_, ok := Find(nums, findFunc)
|
|
||||||
|
|
||||||
assert := internal.NewAssert(t, "TestFindFoundNothing")
|
|
||||||
assert.Equal(false, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFlatten(t *testing.T) {
|
func TestFlatten(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -396,6 +479,73 @@ func TestForEach(t *testing.T) {
|
|||||||
assert.Equal([]int{3, 4, 5, 6, 7}, result)
|
assert.Equal([]int{3, 4, 5, 6, 7}, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestForEachConcurrent(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestForEachConcurrent")
|
||||||
|
|
||||||
|
t.Run("single thread", func(t *testing.T) {
|
||||||
|
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||||
|
result := make([]int, len(numbers))
|
||||||
|
|
||||||
|
addOne := func(index int, value int) {
|
||||||
|
result[index] = value + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEachConcurrent(numbers, addOne, 1)
|
||||||
|
|
||||||
|
expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
assert.Equal(expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("normal", func(t *testing.T) {
|
||||||
|
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||||
|
result := make([]int, len(numbers))
|
||||||
|
|
||||||
|
addOne := func(index int, value int) {
|
||||||
|
result[index] = value + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEachConcurrent(numbers, addOne, 4)
|
||||||
|
|
||||||
|
expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
assert.Equal(expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("negative threads", func(t *testing.T) {
|
||||||
|
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||||
|
result := make([]int, len(numbers))
|
||||||
|
|
||||||
|
addOne := func(index int, value int) {
|
||||||
|
result[index] = value + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEachConcurrent(numbers, addOne, -4)
|
||||||
|
|
||||||
|
expected := []int{2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
assert.Equal(expected, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("high number threads", func(t *testing.T) {
|
||||||
|
numbers := make([]int, 1000)
|
||||||
|
for i := range numbers {
|
||||||
|
numbers[i] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]int, len(numbers))
|
||||||
|
|
||||||
|
addOne := func(index int, value int) {
|
||||||
|
result[index] = value + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEachConcurrent(numbers, addOne, 50)
|
||||||
|
|
||||||
|
for i, item := range numbers {
|
||||||
|
assert.Equal(item+1, result[i])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestForEachWithBreak(t *testing.T) {
|
func TestForEachWithBreak(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -506,6 +656,44 @@ func TestReduce(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReduceConcurrent(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestReduceConcurrent")
|
||||||
|
|
||||||
|
t.Run("basic", func(t *testing.T) {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
|
||||||
|
return agg + item
|
||||||
|
}, 4)
|
||||||
|
assert.Equal(55, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("empty slice", func(t *testing.T) {
|
||||||
|
nums := []int{}
|
||||||
|
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
|
||||||
|
return agg + item
|
||||||
|
}, 4)
|
||||||
|
assert.Equal(0, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("single thread", func(t *testing.T) {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
|
||||||
|
return agg + item
|
||||||
|
}, 1)
|
||||||
|
assert.Equal(55, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("negative threads", func(t *testing.T) {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||||
|
result := ReduceConcurrent(nums, 0, func(_ int, item, agg int) int {
|
||||||
|
return agg + item
|
||||||
|
}, -1)
|
||||||
|
assert.Equal(55, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestReduceBy(t *testing.T) {
|
func TestReduceBy(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -514,14 +702,12 @@ func TestReduceBy(t *testing.T) {
|
|||||||
result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int {
|
result1 := ReduceBy([]int{1, 2, 3, 4}, 0, func(_ int, item int, agg int) int {
|
||||||
return agg + item
|
return agg + item
|
||||||
})
|
})
|
||||||
|
assert.Equal(10, result1)
|
||||||
|
|
||||||
result2 := ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string {
|
result2 := ReduceBy([]int{1, 2, 3, 4}, "", func(_ int, item int, agg string) string {
|
||||||
return agg + fmt.Sprintf("%v", item)
|
return agg + fmt.Sprintf("%v", item)
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.Equal(10, result1)
|
|
||||||
assert.Equal("1234", result2)
|
assert.Equal("1234", result2)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReduceRight(t *testing.T) {
|
func TestReduceRight(t *testing.T) {
|
||||||
@@ -573,15 +759,23 @@ func TestDeleteAt(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
assert := internal.NewAssert(t, "TestDeleteAt")
|
assert := internal.NewAssert(t, "TestDeleteAt")
|
||||||
arr := []int{1, 2, 3, 4, 5}
|
|
||||||
|
|
||||||
assert.Equal([]int{2, 3, 4, 5}, DeleteAt(arr, 0))
|
tests := []struct {
|
||||||
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 4))
|
slice []int
|
||||||
|
deletePos int
|
||||||
|
wang []int
|
||||||
|
}{
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 0, []int{2, 3, 4, 5}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 1, []int{1, 3, 4, 5}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 2, []int{1, 2, 4, 5}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 3, []int{1, 2, 3, 5}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 4, []int{1, 2, 3, 4}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 5, []int{1, 2, 3, 4}},
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 5))
|
for _, tt := range tests {
|
||||||
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 6))
|
assert.Equal(tt.wang, DeleteAt(tt.slice, tt.deletePos))
|
||||||
|
}
|
||||||
assert.Equal([]int{1, 2, 3, 4, 5}, arr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteRange(t *testing.T) {
|
func TestDeleteRange(t *testing.T) {
|
||||||
@@ -601,16 +795,24 @@ func TestDrop(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestDrop")
|
assert := internal.NewAssert(t, "TestDrop")
|
||||||
|
|
||||||
assert.Equal([]int{}, Drop([]int{}, 0))
|
tests := []struct {
|
||||||
assert.Equal([]int{}, Drop([]int{}, 1))
|
slice []int
|
||||||
assert.Equal([]int{}, Drop([]int{}, -1))
|
dropNum int
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{[]int{}, 0, []int{}},
|
||||||
|
{[]int{}, 1, []int{}},
|
||||||
|
{[]int{}, -1, []int{}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 0, []int{1, 2, 3, 4, 5}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 1, []int{2, 3, 4, 5}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 5, []int{}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 6, []int{}},
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 0))
|
for _, tt := range tests {
|
||||||
assert.Equal([]int{2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, 1))
|
assert.Equal(tt.want, Drop(tt.slice, tt.dropNum))
|
||||||
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 5))
|
}
|
||||||
assert.Equal([]int{}, Drop([]int{1, 2, 3, 4, 5}, 6))
|
|
||||||
|
|
||||||
assert.Equal([]int{1, 2, 3, 4, 5}, Drop([]int{1, 2, 3, 4, 5}, -1))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDropRight(t *testing.T) {
|
func TestDropRight(t *testing.T) {
|
||||||
@@ -618,16 +820,23 @@ func TestDropRight(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestDropRight")
|
assert := internal.NewAssert(t, "TestDropRight")
|
||||||
|
|
||||||
assert.Equal([]int{}, DropRight([]int{}, 0))
|
tests := []struct {
|
||||||
assert.Equal([]int{}, DropRight([]int{}, 1))
|
slice []int
|
||||||
assert.Equal([]int{}, DropRight([]int{}, -1))
|
dropNum int
|
||||||
|
want []int
|
||||||
assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, 0))
|
}{
|
||||||
assert.Equal([]int{1, 2, 3, 4}, DropRight([]int{1, 2, 3, 4, 5}, 1))
|
{[]int{}, 0, []int{}},
|
||||||
assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 5))
|
{[]int{}, 1, []int{}},
|
||||||
assert.Equal([]int{}, DropRight([]int{1, 2, 3, 4, 5}, 6))
|
{[]int{}, -1, []int{}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}},
|
||||||
assert.Equal([]int{1, 2, 3, 4, 5}, DropRight([]int{1, 2, 3, 4, 5}, -1))
|
{[]int{1, 2, 3, 4, 5}, 0, []int{1, 2, 3, 4, 5}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, 1, []int{1, 2, 3, 4}},
|
||||||
|
{[]int{}, 5, []int{}},
|
||||||
|
{[]int{}, 6, []int{}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
assert.Equal(tt.want, DropRight(tt.slice, tt.dropNum))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDropWhile(t *testing.T) {
|
func TestDropWhile(t *testing.T) {
|
||||||
@@ -635,22 +844,19 @@ func TestDropWhile(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestDropWhile")
|
assert := internal.NewAssert(t, "TestDropWhile")
|
||||||
|
|
||||||
numbers := []int{1, 2, 3, 4, 5}
|
tests := []struct {
|
||||||
|
slice []int
|
||||||
|
fn func(int) bool
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n != 2 }, []int{2, 3, 4, 5}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return true }, []int{}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 0 }, []int{1, 2, 3, 4, 5}},
|
||||||
|
}
|
||||||
|
|
||||||
r1 := DropWhile(numbers, func(n int) bool {
|
for _, tt := range tests {
|
||||||
return n != 2
|
assert.Equal(tt.want, DropWhile(tt.slice, tt.fn))
|
||||||
})
|
}
|
||||||
assert.Equal([]int{2, 3, 4, 5}, r1)
|
|
||||||
|
|
||||||
r2 := DropWhile(numbers, func(n int) bool {
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
assert.Equal([]int{}, r2)
|
|
||||||
|
|
||||||
r3 := DropWhile(numbers, func(n int) bool {
|
|
||||||
return n == 0
|
|
||||||
})
|
|
||||||
assert.Equal([]int{1, 2, 3, 4, 5}, r3)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDropRightWhile(t *testing.T) {
|
func TestDropRightWhile(t *testing.T) {
|
||||||
@@ -658,22 +864,19 @@ func TestDropRightWhile(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestDropRightWhile")
|
assert := internal.NewAssert(t, "TestDropRightWhile")
|
||||||
|
|
||||||
numbers := []int{1, 2, 3, 4, 5}
|
tests := []struct {
|
||||||
|
slice []int
|
||||||
|
fn func(int) bool
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n != 2 }, []int{1, 2}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return true }, []int{}},
|
||||||
|
{[]int{1, 2, 3, 4, 5}, func(n int) bool { return n == 0 }, []int{1, 2, 3, 4, 5}},
|
||||||
|
}
|
||||||
|
|
||||||
r1 := DropRightWhile(numbers, func(n int) bool {
|
for _, tt := range tests {
|
||||||
return n != 2
|
assert.Equal(tt.want, DropRightWhile(tt.slice, tt.fn))
|
||||||
})
|
}
|
||||||
assert.Equal([]int{1, 2}, r1)
|
|
||||||
|
|
||||||
r2 := DropRightWhile(numbers, func(n int) bool {
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
assert.Equal([]int{}, r2)
|
|
||||||
|
|
||||||
r3 := DropRightWhile(numbers, func(n int) bool {
|
|
||||||
return n == 0
|
|
||||||
})
|
|
||||||
assert.Equal([]int{1, 2, 3, 4, 5}, r3)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsertAt(t *testing.T) {
|
func TestInsertAt(t *testing.T) {
|
||||||
@@ -681,15 +884,25 @@ func TestInsertAt(t *testing.T) {
|
|||||||
|
|
||||||
assert := internal.NewAssert(t, "TestInsertAt")
|
assert := internal.NewAssert(t, "TestInsertAt")
|
||||||
|
|
||||||
strs := []string{"a", "b", "c"}
|
tests := []struct {
|
||||||
assert.Equal([]string{"a", "b", "c"}, InsertAt(strs, -1, "1"))
|
slice []string
|
||||||
assert.Equal([]string{"a", "b", "c"}, InsertAt(strs, 4, "1"))
|
insertPos int
|
||||||
assert.Equal([]string{"1", "a", "b", "c"}, InsertAt(strs, 0, "1"))
|
insertValue any
|
||||||
assert.Equal([]string{"a", "1", "b", "c"}, InsertAt(strs, 1, "1"))
|
want []string
|
||||||
assert.Equal([]string{"a", "b", "1", "c"}, InsertAt(strs, 2, "1"))
|
}{
|
||||||
assert.Equal([]string{"a", "b", "c", "1"}, InsertAt(strs, 3, "1"))
|
{[]string{"a", "b", "c"}, -1, "1", []string{"a", "b", "c"}},
|
||||||
assert.Equal([]string{"1", "2", "3", "a", "b", "c"}, InsertAt(strs, 0, []string{"1", "2", "3"}))
|
{[]string{"a", "b", "c"}, 4, "1", []string{"a", "b", "c"}},
|
||||||
assert.Equal([]string{"a", "b", "c", "1", "2", "3"}, InsertAt(strs, 3, []string{"1", "2", "3"}))
|
{[]string{"a", "b", "c"}, 0, "1", []string{"1", "a", "b", "c"}},
|
||||||
|
{[]string{"a", "b", "c"}, 1, "1", []string{"a", "1", "b", "c"}},
|
||||||
|
{[]string{"a", "b", "c"}, 2, "1", []string{"a", "b", "1", "c"}},
|
||||||
|
{[]string{"a", "b", "c"}, 3, "1", []string{"a", "b", "c", "1"}},
|
||||||
|
{[]string{"a", "b", "c"}, 0, []string{"1", "2", "3"}, []string{"1", "2", "3", "a", "b", "c"}},
|
||||||
|
{[]string{"a", "b", "c"}, 3, []string{"1", "2", "3"}, []string{"a", "b", "c", "1", "2", "3"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
assert.Equal(tt.want, InsertAt(tt.slice, tt.insertPos, tt.insertValue))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateAt(t *testing.T) {
|
func TestUpdateAt(t *testing.T) {
|
||||||
@@ -720,7 +933,87 @@ func TestUniqueBy(t *testing.T) {
|
|||||||
actual := UniqueBy([]int{1, 2, 3, 4, 5, 6}, func(val int) int {
|
actual := UniqueBy([]int{1, 2, 3, 4, 5, 6}, func(val int) int {
|
||||||
return val % 4
|
return val % 4
|
||||||
})
|
})
|
||||||
assert.Equal([]int{1, 2, 3, 0}, actual)
|
|
||||||
|
assert.Equal([]int{1, 2, 3, 4}, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUniqueByField(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestUniqueByField")
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []User{
|
||||||
|
{ID: 1, Name: "a"},
|
||||||
|
{ID: 2, Name: "b"},
|
||||||
|
{ID: 1, Name: "c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueUsers, err := UniqueByField(users, "ID")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal([]User{
|
||||||
|
{ID: 1, Name: "a"},
|
||||||
|
{ID: 2, Name: "b"},
|
||||||
|
}, uniqueUsers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUniqueByComparator(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestUniqueByComparator")
|
||||||
|
|
||||||
|
t.Run("equal comparison", func(t *testing.T) {
|
||||||
|
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
|
||||||
|
comparator := func(item int, other int) bool {
|
||||||
|
return item == other
|
||||||
|
}
|
||||||
|
result := UniqueByComparator(nums, comparator)
|
||||||
|
|
||||||
|
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7}, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unique struct slice by field", func(t *testing.T) {
|
||||||
|
type student struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
students := []student{
|
||||||
|
{Name: "a", Age: 11},
|
||||||
|
{Name: "b", Age: 12},
|
||||||
|
{Name: "a", Age: 13},
|
||||||
|
{Name: "c", Age: 14},
|
||||||
|
}
|
||||||
|
|
||||||
|
comparator := func(item, other student) bool { return item.Name == other.Name }
|
||||||
|
|
||||||
|
result := UniqueByComparator(students, comparator)
|
||||||
|
|
||||||
|
assert.Equal([]student{
|
||||||
|
{Name: "a", Age: 11},
|
||||||
|
{Name: "b", Age: 12},
|
||||||
|
{Name: "c", Age: 14},
|
||||||
|
}, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("case-insensitive string comparison", func(t *testing.T) {
|
||||||
|
stringSlice := []string{"apple", "banana", "Apple", "cherry", "Banana", "date"}
|
||||||
|
caseInsensitiveComparator := func(item, other string) bool {
|
||||||
|
return strings.ToLower(item) == strings.ToLower(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := UniqueByComparator(stringSlice, caseInsensitiveComparator)
|
||||||
|
|
||||||
|
assert.Equal([]string{"apple", "banana", "cherry", "date"}, result)
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnion(t *testing.T) {
|
func TestUnion(t *testing.T) {
|
||||||
@@ -1357,3 +1650,119 @@ 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUniqueByConcurrent(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestUniqueByConcurrent")
|
||||||
|
|
||||||
|
nums := []int{1, 2, 3, 1, 2, 4, 5, 6, 4, 7}
|
||||||
|
comparator := func(item int, other int) bool { return item == other }
|
||||||
|
|
||||||
|
result := UniqueByConcurrent(nums, comparator, 4)
|
||||||
|
|
||||||
|
assert.Equal([]int{1, 2, 3, 4, 5, 6, 7}, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapConcurrent(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestMapConcurrent")
|
||||||
|
|
||||||
|
t.Run("empty slice", func(t *testing.T) {
|
||||||
|
actual := MapConcurrent([]int{}, func(_, n int) int { return n * n }, 4)
|
||||||
|
assert.Equal([]int{}, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("single thread", func(t *testing.T) {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
expected := []int{1, 4, 9, 16, 25, 36}
|
||||||
|
actual := MapConcurrent(nums, func(_, n int) int { return n * n }, 1)
|
||||||
|
assert.Equal(expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("multiple threads", func(t *testing.T) {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
expected := []int{1, 4, 9, 16, 25, 36}
|
||||||
|
actual := MapConcurrent(nums, func(_, n int) int { return n * n }, 4)
|
||||||
|
assert.Equal(expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilterConcurrent(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
assert := internal.NewAssert(t, "TestFilterConcurrent")
|
||||||
|
|
||||||
|
t.Run("empty slice", func(t *testing.T) {
|
||||||
|
actual := FilterConcurrent([]int{}, func(_, n int) bool { return n != 0 }, 4)
|
||||||
|
assert.Equal([]int{}, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("single thread", func(t *testing.T) {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
expected := []int{4, 5, 6}
|
||||||
|
actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 1)
|
||||||
|
assert.Equal(expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("multiple threads", func(t *testing.T) {
|
||||||
|
nums := []int{1, 2, 3, 4, 5, 6}
|
||||||
|
expected := []int{4, 5, 6}
|
||||||
|
actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 4)
|
||||||
|
assert.Equal(expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,7 +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: todo
|
// 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")
|
||||||
@@ -617,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]")
|
||||||
@@ -213,7 +214,7 @@ func IsIpV4(ipstr string) bool {
|
|||||||
if ip == nil {
|
if ip == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return strings.Contains(ipstr, ".")
|
return ip.To4() != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsIpV6 check if the string is a ipv6 address.
|
// IsIpV6 check if the string is a ipv6 address.
|
||||||
@@ -223,7 +224,7 @@ func IsIpV6(ipstr string) bool {
|
|||||||
if ip == nil {
|
if ip == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return strings.Contains(ipstr, ":")
|
return ip.To4() == nil && len(ip) == net.IPv6len
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPort check if the string is a valid net port.
|
// IsPort check if the string is a valid net port.
|
||||||
@@ -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