mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
42 Commits
860a499f98
...
v2.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
036847577d | ||
|
|
d21edd1cde | ||
|
|
c58c50327c | ||
|
|
9bfdc686f8 | ||
|
|
5ca8f6ef6f | ||
|
|
a54d4c79a0 | ||
|
|
f7b54986aa | ||
|
|
92fae4273b | ||
|
|
0d29f5437a | ||
|
|
e95d7c82cd | ||
|
|
e138043289 | ||
|
|
aabfcb7bde | ||
|
|
0b5e884371 | ||
|
|
3d1bd08434 | ||
|
|
a62ad71791 | ||
|
|
c02c4f813b | ||
|
|
235d2f2486 | ||
|
|
e9380a3d9f | ||
|
|
81d13c2f1a | ||
|
|
7290296849 | ||
|
|
8a8460a592 | ||
|
|
7a98c431d3 | ||
|
|
606d887230 | ||
|
|
473f9c9f3e | ||
|
|
9ff3d0e79c | ||
|
|
5db1d07d6d | ||
|
|
6c6d14828a | ||
|
|
0e1593c67b | ||
|
|
6c7f38d8b3 | ||
|
|
069812e0ee | ||
|
|
4a539a23c8 | ||
|
|
0b1dab0399 | ||
|
|
805e2543d0 | ||
|
|
a3d518da76 | ||
|
|
e3e2d8394c | ||
|
|
0eeaa06055 | ||
|
|
a43bc554ee | ||
|
|
aebab7c944 | ||
|
|
665bad4ca3 | ||
|
|
e4901e99e9 | ||
|
|
4277e8eca5 | ||
|
|
fdc93c8cc7 |
2
.github/workflows/codecov.yml
vendored
2
.github/workflows/codecov.yml
vendored
@@ -3,9 +3,11 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- rc
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- rc
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -30,7 +30,7 @@ We are excited that you are interested in contributing to lancet. Before submitt
|
||||
|
||||
- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass.
|
||||
|
||||
- Make sure PRs are created to `v2` branch instead of `master` branch.
|
||||
- Make sure PRs are created to `rc` branch instead of other branch.
|
||||
|
||||
- If your PR fixes a bug, please provide a description about the related bug.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代
|
||||
|
||||
- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。
|
||||
|
||||
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
|
||||
- 确保 PR 是提交到 `rc` 分支,而不是其他分支。
|
||||
|
||||
- 如果是修复 bug,请在 PR 中给出描述信息。
|
||||
|
||||
|
||||
64
README.md
64
README.md
@@ -4,7 +4,7 @@
|
||||
<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://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -38,7 +38,7 @@
|
||||
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
|
||||
```
|
||||
|
||||
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.2. </b>
|
||||
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.3. </b>
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x
|
||||
@@ -318,6 +318,14 @@ import "github.com/duke-git/lancet/v2/convertor"
|
||||
- **<big>GbkToUtf8</big>** : converts GBK encoding data to utf8 encoding data.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#GbkToUtf8)]
|
||||
[[play](https://go.dev/play/p/OphmHCN_9u8)]
|
||||
- **<big>ToStdBase64</big>** : converts a value to a string encoded in standard Base64.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToStdBase64)]
|
||||
- **<big>ToUrlBase64</big>** : converts a value to a string encoded in url Base64.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToUrlBase64)]
|
||||
- **<big>ToRawStdBase64</big>** : converts a value to a string encoded in raw standard Base64.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawStdBase64)]
|
||||
- **<big>ToRawUrlBase64</big>** : converts a value to a string encoded in raw url Base64.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawUrlBase64)]
|
||||
|
||||
<h3 id="cryptor"> 6. Cryptor package is for data encryption and decryption. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -608,6 +616,7 @@ import set "github.com/duke-git/lancet/v2/datastructure/set"
|
||||
import tree "github.com/duke-git/lancet/v2/datastructure/tree"
|
||||
import heap "github.com/duke-git/lancet/v2/datastructure/heap"
|
||||
import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
import optional "github.com/duke-git/lancet/v2/datastructure/optional"
|
||||
```
|
||||
|
||||
#### Structure list:
|
||||
@@ -630,6 +639,9 @@ import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/heap.md)]
|
||||
- **<big>Hashmap</big>** : hash map structure.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/hashmap.md)]
|
||||
- **<big>Optional</big>** : Optional container.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/optional.md)]
|
||||
|
||||
|
||||
<h3 id="fileutil"> 9. Fileutil package implements some basic functions for file operations. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -717,6 +729,10 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
|
||||
- **<big>ReadFile</big>** : read file or url.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ReadFile)]
|
||||
- **<big>ChunkRead</big>** : reads a block from the file at the specified offset and returns all lines within the block.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ChunkRead)]
|
||||
- **<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)]
|
||||
|
||||
<h3 id="formatter"> 10. Formatter contains some functions for data formatting. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -779,11 +795,25 @@ import "github.com/duke-git/lancet/v2/function"
|
||||
[[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.
|
||||
[[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.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#AcceptIf)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
|
||||
|
||||
|
||||
<h3 id="maputil"> 12. Maputil package includes some functions to manipulate map. <a href="#index">index</a></h3>
|
||||
|
||||
```go
|
||||
@@ -927,6 +957,14 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
||||
- **<big>TruncRound</big>** : round off n decimal places for int64.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#TruncRound)]
|
||||
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
||||
- **<big>CeilToFloat</big>** : round float up n decimal places.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#CeilToFloat)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
- **<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)]
|
||||
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
|
||||
@@ -966,6 +1004,9 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
||||
- **<big>Abs</big>** : returns the absolute value of param number.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)]
|
||||
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
|
||||
- **<big>Div</big>** : returns the result of x divided by y.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Div)]
|
||||
|
||||
|
||||
<h3 id="netutil"> 14. Netutil package contains functions to get net information and send http request. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -1135,6 +1176,16 @@ import "github.com/duke-git/lancet/v2/retry"
|
||||
- **<big>RetryTimes</big>** : set times of retry.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryTimes)]
|
||||
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
|
||||
- **<big>BackoffStrategy</big>** : An interface that defines a method for calculating backoff intervals.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#BackoffStrategy)]
|
||||
- **<big>RetryWithCustomBackoff</big>** : set abitary custom backoff strategy.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithCustomBackoff)]
|
||||
- **<big>RetryWithLinearBackoff</big>** : set linear strategy backoff.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithLinearBackoff)]
|
||||
- **<big>RetryWithExponentialWithJitterBackoff</big>** : set exponential strategy backoff.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
|
||||
|
||||
|
||||
|
||||
<h3 id="slice"> 18. Slice contains some functions to manipulate slice. <a href="#index">index</a></h3>
|
||||
|
||||
@@ -1360,6 +1411,8 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Random</big>** : get a random item of slice, return its index, when slice is empty, return -1.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Random)]
|
||||
[[play](https://go.dev/play/p/UzpGQptWppw)]
|
||||
- **<big>SetToDefaultIf</big>** : set elements to their default value if they match the given predicate.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#SetToDefaultIf)]
|
||||
|
||||
|
||||
<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>
|
||||
@@ -1605,7 +1658,10 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- **<big>RemoveWhiteSpace</big>** : remove whitespace characters from a string.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#RemoveWhiteSpace)]
|
||||
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
|
||||
|
||||
- **<big>SubInBetween</big>** : return substring between the start and end position(excluded) of source string.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#SubInBetween)]
|
||||
- **<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)]
|
||||
|
||||
<h3 id="system"> 22. System package contain some functions about os, runtime, shell command. <a href="#index">index</a></h3>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<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://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -37,7 +37,7 @@
|
||||
go get github.com/duke-git/lancet/v2 //安装v2最新版本v2.x.x
|
||||
```
|
||||
|
||||
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.2。</b>
|
||||
2. <b>使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.3。</b>
|
||||
|
||||
```go
|
||||
go get github.com/duke-git/lancet// 使用go1.18以下版本, 必须安装v1.x.x版本
|
||||
@@ -100,6 +100,7 @@ func main() {
|
||||
- [Validator](#user-content-validator)
|
||||
- [Xerror](#user-content-xerror)
|
||||
|
||||
|
||||
<h3 id="algorithm"> 1. algorithm 包实现一些基本查找和排序算法。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
```go
|
||||
@@ -317,6 +318,15 @@ import "github.com/duke-git/lancet/v2/convertor"
|
||||
- **<big>GbkToUtf8</big>** : GBK 编码转 utf8 编码。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#GbkToUtf8)]
|
||||
[[play](https://go.dev/play/p/OphmHCN_9u8)]
|
||||
- **<big>ToStdBase64</big>** : 将值转换为StdBase64编码的字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToStdBase64)]
|
||||
- **<big>ToUrlBase64</big>** : 将值转换为url Base64编码的字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToUrlBase64)]
|
||||
- **<big>ToRawStdBase64</big>** : 将值转换为RawStdBase64编码的字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawStdBase64)]
|
||||
- **<big>ToRawUrlBase64</big>** : 将值转换为RawUrlBase64编码的字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/convertor.md#ToRawUrlBase64)]
|
||||
|
||||
|
||||
<h3 id="cryptor"> 6. cryptor 加密包支持数据加密和解密,获取 md5,hash 值。支持 base64, md5, hmac, aes, des, rsa。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
@@ -718,6 +728,12 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
[[play](https://go.dev/play/p/GhLS6d8lH_g)]
|
||||
- **<big>ReadFile</big>** : 读取文件或者URL。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadFile)]
|
||||
- **<big>ChunkRead</big>** : 从文件的指定偏移读取块并返回块内所有行。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ChunkRead)]
|
||||
- **<big>ParallelChunkRead</big>** : 并行读取文件并将每个块的行发送到指定通道。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ParallelChunkRead)]
|
||||
|
||||
|
||||
|
||||
<h3 id="formatter"> 10. formatter 格式化器包含一些数据格式化处理方法。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
@@ -781,6 +797,20 @@ import "github.com/duke-git/lancet/v2/function"
|
||||
- **<big>Pipeline</big>** : 从右至左执行函数列表。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Pipeline)]
|
||||
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
|
||||
- **<big>AcceptIf</big>** : AcceptIf函数会返回另一个函数,该函数的签名与apply函数相同,但同时还会包含一个布尔值来表示成功或失败。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#AcceptIf)]
|
||||
- **<big>And</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑and操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#And)]
|
||||
- **<big>Or</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑or操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Or)]
|
||||
- **<big>Negate</big>** : 返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Negate)]
|
||||
- **<big>Nor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非或nor的操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nor)]
|
||||
- **<big>Nand</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑非与nand的操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nand)]
|
||||
- **<big>Xnor</big>** : 返回一个复合谓词判断函数,该判断函数表示一组谓词的逻辑异或xnor的操作。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Xnor)]
|
||||
- **<big>Watcher</big>** : Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Watcher)]
|
||||
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
|
||||
@@ -928,6 +958,14 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
||||
- **<big>TruncRound</big>** : 截短 n 位小数(不进行四舍五入)。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#TruncRound)]
|
||||
[[play](https://go.dev/play/p/aumarSHIGzP)]
|
||||
- **<big>CeilToFloat</big>** : 向上舍入(进一法),保留n位小数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToFloat)]
|
||||
- **<big>CeilToString</big>** : 向上舍入(进一法),保留n位小数,返回字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToString)]
|
||||
- **<big>FloorToFloat</big>** : 向下舍入(去尾法),保留n位小数。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToFloat)]
|
||||
- **<big>FloorToString</big>** : 向下舍入(去尾法),保留n位小数,返回字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToString)]
|
||||
- **<big>Range</big>** : 根据指定的起始值和数量,创建一个数字切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Range)]
|
||||
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
|
||||
@@ -967,6 +1005,10 @@ import "github.com/duke-git/lancet/v2/mathutil"
|
||||
- **<big>Abs</big>** : 求绝对值。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Sum)]
|
||||
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
|
||||
- **<big>Div</big>** : 除法运算。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Div)]
|
||||
|
||||
|
||||
|
||||
<h3 id="netutil"> 14. netutil 网络包支持获取 ip 地址,发送 http 请求。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
@@ -1134,6 +1176,16 @@ import "github.com/duke-git/lancet/v2/retry"
|
||||
- **<big>RetryTimes</big>** : 设置重试次数,默认 5。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryTimes)]
|
||||
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
|
||||
- **<big>BackoffStrategy</big>** : 定义计算退避间隔的方法的接口。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#BackoffStrategy)]
|
||||
- **<big>RetryWithCustomBackoff</big>** : 设置自定义退避策略。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithCustomBackoff)]
|
||||
- **<big>RetryWithLinearBackoff</big>** : 设置线性策略退避。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithLinearBackoff)]
|
||||
- **<big>RetryWithExponentialWithJitterBackoff</big>** : 设置指数策略退避。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
|
||||
|
||||
|
||||
|
||||
<h3 id="slice"> 18. slice 包含操作切片的方法集合。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
@@ -1358,6 +1410,8 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>Random</big>** : 随机返回切片中元素以及下标, 当切片长度为0时返回下标-1。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Random)]
|
||||
[[play](https://go.dev/play/p/UzpGQptWppw)]
|
||||
- **<big>SetToDefaultIf</big>** : 根据给定给定的predicate判定函数来修改切片中的元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#SetToDefaultIf)]
|
||||
|
||||
|
||||
|
||||
@@ -1607,6 +1661,10 @@ import "github.com/duke-git/lancet/v2/strutil"
|
||||
- **<big>RemoveWhiteSpace</big>** : 删除字符串中的空格。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#RemoveWhiteSpace)]
|
||||
[[play](https://go.dev/play/p/HzLC9vsTwkf)]
|
||||
- **<big>SubInBetween</big>** : 获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#SubInBetween)]
|
||||
- **<big>HammingDistance</big>** : 计算两个字符串之间的汉明距离。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HammingDistance)]
|
||||
|
||||
<h3 id="system"> 22. system 包含 os, runtime, shell command 的相关函数。 <a href="#index">回到目录</a></h3>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package convertor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
@@ -394,3 +395,87 @@ func GbkToUtf8(bs []byte) ([]byte, error) {
|
||||
b, err := io.ReadAll(r)
|
||||
return b, err
|
||||
}
|
||||
|
||||
// ToStdBase64 convert data to standard base64 encoding.
|
||||
func ToStdBase64(value any) string {
|
||||
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||
return ""
|
||||
}
|
||||
switch value.(type) {
|
||||
case []byte:
|
||||
return base64.StdEncoding.EncodeToString(value.([]byte))
|
||||
case string:
|
||||
return base64.StdEncoding.EncodeToString([]byte(value.(string)))
|
||||
case error:
|
||||
return base64.StdEncoding.EncodeToString([]byte(value.(error).Error()))
|
||||
default:
|
||||
marshal, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(marshal)
|
||||
}
|
||||
}
|
||||
|
||||
// ToUrlBase64 convert data to URL base64 encoding.
|
||||
func ToUrlBase64(value any) string {
|
||||
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||
return ""
|
||||
}
|
||||
switch value.(type) {
|
||||
case []byte:
|
||||
return base64.URLEncoding.EncodeToString(value.([]byte))
|
||||
case string:
|
||||
return base64.URLEncoding.EncodeToString([]byte(value.(string)))
|
||||
case error:
|
||||
return base64.URLEncoding.EncodeToString([]byte(value.(error).Error()))
|
||||
default:
|
||||
marshal, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(marshal)
|
||||
}
|
||||
}
|
||||
|
||||
// ToRawStdBase64 convert data to raw standard base64 encoding.
|
||||
func ToRawStdBase64(value any) string {
|
||||
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||
return ""
|
||||
}
|
||||
switch value.(type) {
|
||||
case []byte:
|
||||
return base64.RawStdEncoding.EncodeToString(value.([]byte))
|
||||
case string:
|
||||
return base64.RawStdEncoding.EncodeToString([]byte(value.(string)))
|
||||
case error:
|
||||
return base64.RawStdEncoding.EncodeToString([]byte(value.(error).Error()))
|
||||
default:
|
||||
marshal, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return base64.RawStdEncoding.EncodeToString(marshal)
|
||||
}
|
||||
}
|
||||
|
||||
// ToRawUrlBase64 convert data to raw URL base64 encoding.
|
||||
func ToRawUrlBase64(value any) string {
|
||||
if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) {
|
||||
return ""
|
||||
}
|
||||
switch value.(type) {
|
||||
case []byte:
|
||||
return base64.RawURLEncoding.EncodeToString(value.([]byte))
|
||||
case string:
|
||||
return base64.RawURLEncoding.EncodeToString([]byte(value.(string)))
|
||||
case error:
|
||||
return base64.RawURLEncoding.EncodeToString([]byte(value.(error).Error()))
|
||||
default:
|
||||
marshal, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(marshal)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package convertor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -391,3 +392,182 @@ func ExampleGbkToUtf8() {
|
||||
// true
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleToStdBase64() {
|
||||
// if you want to see the result, please use 'base64.StdEncoding.DecodeString()' to decode the result
|
||||
|
||||
afterEncode := ToStdBase64(nil)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode = ToStdBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = ToStdBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = ToStdBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = ToStdBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = ToStdBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = ToStdBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = ToStdBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
//
|
||||
// aGVsbG8=
|
||||
// aGVsbG8=
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||
// MTIzLjQ1Ng==
|
||||
// dHJ1ZQ==
|
||||
// ZXJy
|
||||
}
|
||||
|
||||
func ExampleToUrlBase64() {
|
||||
// if you want to see the result, please use 'base64.URLEncoding.DecodeString()' to decode the result
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode := ToUrlBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = ToUrlBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = ToUrlBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = ToUrlBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = ToUrlBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = ToUrlBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = ToUrlBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
// aGVsbG8=
|
||||
// aGVsbG8=
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||
// MTIzLjQ1Ng==
|
||||
// dHJ1ZQ==
|
||||
// ZXJy
|
||||
}
|
||||
|
||||
func ExampleToRawStdBase64() {
|
||||
// if you want to see the result, please use 'base64.RawStdEncoding.DecodeString()' to decode the result
|
||||
stringVal := "hello"
|
||||
afterEncode := ToRawStdBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = ToRawStdBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = ToRawStdBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = ToRawStdBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = ToRawStdBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = ToRawStdBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = ToRawStdBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
// aGVsbG8
|
||||
// aGVsbG8
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||
// MTIzLjQ1Ng
|
||||
// dHJ1ZQ
|
||||
// ZXJy
|
||||
}
|
||||
|
||||
func ExampleToRawUrlBase64() {
|
||||
// if you want to see the result, please use 'base64.RawURLEncoding.DecodeString()' to decode the result
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode := ToRawUrlBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = ToRawUrlBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = ToRawUrlBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = ToRawUrlBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = ToRawUrlBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = ToRawUrlBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = ToRawUrlBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
// aGVsbG8
|
||||
// aGVsbG8
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||
// MTIzLjQ1Ng
|
||||
// dHJ1ZQ
|
||||
// ZXJy
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package convertor
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
@@ -461,3 +465,279 @@ func TestGbkToUtf8(t *testing.T) {
|
||||
assert.Equal(true, utf8.Valid(utf8Data))
|
||||
assert.Equal("hello", string(utf8Data))
|
||||
}
|
||||
|
||||
func TestToStdBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestToStdBase64")
|
||||
|
||||
r1 := ToStdBase64("abc")
|
||||
d1, _ := base64.StdEncoding.DecodeString(r1)
|
||||
assert.Equal("abc", string(d1))
|
||||
|
||||
r2 := ToStdBase64([]byte("abc"))
|
||||
d2, _ := base64.StdEncoding.DecodeString(r2)
|
||||
assert.Equal("abc", string(d2))
|
||||
|
||||
r3 := ToStdBase64(123)
|
||||
d3, _ := base64.StdEncoding.DecodeString(r3)
|
||||
assert.Equal("123", string(d3))
|
||||
|
||||
r4 := ToStdBase64(11.11)
|
||||
d4, _ := base64.StdEncoding.DecodeString(r4)
|
||||
assert.Equal("11.11", string(d4))
|
||||
|
||||
r5 := ToStdBase64(map[string]any{"name": "duke", "quantity": 1})
|
||||
d5, _ := base64.StdEncoding.DecodeString(r5)
|
||||
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
|
||||
|
||||
r6 := ToStdBase64([]int64{7, 5, 9, 4, 23})
|
||||
d6, _ := base64.StdEncoding.DecodeString(r6)
|
||||
assert.Equal("[7,5,9,4,23]", string(d6))
|
||||
|
||||
r7 := ToStdBase64([]string{"7", "5", "9", "4", "23"})
|
||||
d7, _ := base64.StdEncoding.DecodeString(r7)
|
||||
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
|
||||
|
||||
r8 := ToStdBase64(nil)
|
||||
d8, _ := base64.StdEncoding.DecodeString(r8)
|
||||
assert.Equal("", string(d8))
|
||||
|
||||
ch := make(chan int, 3)
|
||||
ch <- 1
|
||||
ch <- 2
|
||||
r9 := ToStdBase64(ch)
|
||||
d9, _ := base64.StdEncoding.DecodeString(r9)
|
||||
assert.Equal("", string(d9))
|
||||
|
||||
r10 := ToStdBase64(io.EOF)
|
||||
d10, _ := base64.StdEncoding.DecodeString(r10)
|
||||
assert.Equal("EOF", string(d10))
|
||||
|
||||
r11 := ToStdBase64(errors.New("test"))
|
||||
d11, _ := base64.StdEncoding.DecodeString(r11)
|
||||
assert.Equal("test", string(d11))
|
||||
|
||||
typedNil := (*int)(nil)
|
||||
r12 := ToStdBase64(typedNil)
|
||||
d12, _ := base64.StdEncoding.DecodeString(r12)
|
||||
assert.Equal("", string(d12))
|
||||
|
||||
type nilInterface interface {
|
||||
}
|
||||
var nI nilInterface = nil
|
||||
d13, _ := base64.StdEncoding.DecodeString(ToStdBase64(nI))
|
||||
assert.Equal("", string(d13))
|
||||
|
||||
var p unsafe.Pointer
|
||||
d14, _ := base64.StdEncoding.DecodeString(ToStdBase64(p))
|
||||
assert.Equal("", string(d14))
|
||||
}
|
||||
|
||||
func TestToUrlBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestToUrlBase64")
|
||||
|
||||
r1 := ToUrlBase64("abc")
|
||||
d1, _ := base64.URLEncoding.DecodeString(r1)
|
||||
assert.Equal("abc", string(d1))
|
||||
|
||||
r2 := ToUrlBase64([]byte("abc"))
|
||||
d2, _ := base64.URLEncoding.DecodeString(r2)
|
||||
assert.Equal("abc", string(d2))
|
||||
|
||||
r3 := ToUrlBase64(123)
|
||||
d3, _ := base64.URLEncoding.DecodeString(r3)
|
||||
assert.Equal("123", string(d3))
|
||||
|
||||
r4 := ToUrlBase64(11.11)
|
||||
d4, _ := base64.URLEncoding.DecodeString(r4)
|
||||
assert.Equal("11.11", string(d4))
|
||||
|
||||
r5 := ToUrlBase64(map[string]any{"name": "duke", "quantity": 1})
|
||||
d5, _ := base64.URLEncoding.DecodeString(r5)
|
||||
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
|
||||
|
||||
r6 := ToUrlBase64([]int64{7, 5, 9, 4, 23})
|
||||
d6, _ := base64.URLEncoding.DecodeString(r6)
|
||||
assert.Equal("[7,5,9,4,23]", string(d6))
|
||||
|
||||
r7 := ToUrlBase64([]string{"7", "5", "9", "4", "23"})
|
||||
d7, _ := base64.URLEncoding.DecodeString(r7)
|
||||
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
|
||||
|
||||
r8 := ToUrlBase64(nil)
|
||||
d8, _ := base64.URLEncoding.DecodeString(r8)
|
||||
assert.Equal("", string(d8))
|
||||
|
||||
ch := make(chan int, 3)
|
||||
ch <- 1
|
||||
ch <- 2
|
||||
r9 := ToUrlBase64(ch)
|
||||
d9, _ := base64.URLEncoding.DecodeString(r9)
|
||||
assert.Equal("", string(d9))
|
||||
|
||||
r10 := ToUrlBase64(io.EOF)
|
||||
d10, _ := base64.URLEncoding.DecodeString(r10)
|
||||
assert.Equal("EOF", string(d10))
|
||||
|
||||
r11 := ToUrlBase64(errors.New("test"))
|
||||
d11, _ := base64.URLEncoding.DecodeString(r11)
|
||||
assert.Equal("test", string(d11))
|
||||
|
||||
typedNil := (*int)(nil)
|
||||
r12 := ToUrlBase64(typedNil)
|
||||
d12, _ := base64.URLEncoding.DecodeString(r12)
|
||||
assert.Equal("", string(d12))
|
||||
|
||||
type nilInterface interface {
|
||||
}
|
||||
var nI nilInterface = nil
|
||||
d13, _ := base64.URLEncoding.DecodeString(ToUrlBase64(nI))
|
||||
assert.Equal("", string(d13))
|
||||
|
||||
var p unsafe.Pointer
|
||||
d14, _ := base64.URLEncoding.DecodeString(ToUrlBase64(p))
|
||||
assert.Equal("", string(d14))
|
||||
|
||||
r15 := ToUrlBase64("4+3/4?=")
|
||||
d15, _ := base64.URLEncoding.DecodeString(r15)
|
||||
assert.Equal("4+3/4?=", string(d15))
|
||||
}
|
||||
|
||||
func TestToRawStdBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestToRawStdBase64")
|
||||
|
||||
r1 := ToRawStdBase64("abc")
|
||||
d1, _ := base64.RawStdEncoding.DecodeString(r1)
|
||||
assert.Equal("abc", string(d1))
|
||||
|
||||
r2 := ToRawStdBase64([]byte("abc"))
|
||||
d2, _ := base64.RawStdEncoding.DecodeString(r2)
|
||||
assert.Equal("abc", string(d2))
|
||||
|
||||
r3 := ToRawStdBase64(123)
|
||||
d3, _ := base64.RawStdEncoding.DecodeString(r3)
|
||||
assert.Equal("123", string(d3))
|
||||
|
||||
r4 := ToRawStdBase64(11.11)
|
||||
d4, _ := base64.RawStdEncoding.DecodeString(r4)
|
||||
assert.Equal("11.11", string(d4))
|
||||
|
||||
r5 := ToRawStdBase64(map[string]any{"name": "duke", "quantity": 1})
|
||||
d5, _ := base64.RawStdEncoding.DecodeString(r5)
|
||||
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
|
||||
|
||||
r6 := ToRawStdBase64([]int64{7, 5, 9, 4, 23})
|
||||
d6, _ := base64.RawStdEncoding.DecodeString(r6)
|
||||
assert.Equal("[7,5,9,4,23]", string(d6))
|
||||
|
||||
r7 := ToRawStdBase64([]string{"7", "5", "9", "4", "23"})
|
||||
d7, _ := base64.RawStdEncoding.DecodeString(r7)
|
||||
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
|
||||
|
||||
r8 := ToRawStdBase64(nil)
|
||||
d8, _ := base64.RawStdEncoding.DecodeString(r8)
|
||||
assert.Equal("", string(d8))
|
||||
|
||||
ch := make(chan int, 3)
|
||||
ch <- 1
|
||||
ch <- 2
|
||||
r9 := ToRawStdBase64(ch)
|
||||
d9, _ := base64.RawStdEncoding.DecodeString(r9)
|
||||
assert.Equal("", string(d9))
|
||||
|
||||
r10 := ToRawStdBase64(io.EOF)
|
||||
d10, _ := base64.RawStdEncoding.DecodeString(r10)
|
||||
assert.Equal("EOF", string(d10))
|
||||
|
||||
r11 := ToRawStdBase64(errors.New("test"))
|
||||
d11, _ := base64.RawStdEncoding.DecodeString(r11)
|
||||
assert.Equal("test", string(d11))
|
||||
|
||||
typedNil := (*int)(nil)
|
||||
r12 := ToRawStdBase64(typedNil)
|
||||
d12, _ := base64.RawStdEncoding.DecodeString(r12)
|
||||
assert.Equal("", string(d12))
|
||||
|
||||
type nilInterface interface {
|
||||
}
|
||||
var nI nilInterface = nil
|
||||
d13, _ := base64.RawStdEncoding.DecodeString(ToRawStdBase64(nI))
|
||||
assert.Equal("", string(d13))
|
||||
|
||||
var p unsafe.Pointer
|
||||
d14, _ := base64.RawStdEncoding.DecodeString(ToRawStdBase64(p))
|
||||
assert.Equal("", string(d14))
|
||||
}
|
||||
|
||||
func TestToRawUrlBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestToRawUrlBase64")
|
||||
|
||||
r1 := ToRawUrlBase64("abc")
|
||||
d1, _ := base64.RawURLEncoding.DecodeString(r1)
|
||||
assert.Equal("abc", string(d1))
|
||||
|
||||
r2 := ToRawUrlBase64([]byte("abc"))
|
||||
d2, _ := base64.RawURLEncoding.DecodeString(r2)
|
||||
assert.Equal("abc", string(d2))
|
||||
|
||||
r3 := ToRawUrlBase64(123)
|
||||
d3, _ := base64.RawURLEncoding.DecodeString(r3)
|
||||
assert.Equal("123", string(d3))
|
||||
|
||||
r4 := ToRawUrlBase64(11.11)
|
||||
d4, _ := base64.RawURLEncoding.DecodeString(r4)
|
||||
assert.Equal("11.11", string(d4))
|
||||
|
||||
r5 := ToRawUrlBase64(map[string]any{"name": "duke", "quantity": 1})
|
||||
d5, _ := base64.RawURLEncoding.DecodeString(r5)
|
||||
assert.Equal("{\"name\":\"duke\",\"quantity\":1}", string(d5))
|
||||
|
||||
r6 := ToRawUrlBase64([]int64{7, 5, 9, 4, 23})
|
||||
d6, _ := base64.RawURLEncoding.DecodeString(r6)
|
||||
assert.Equal("[7,5,9,4,23]", string(d6))
|
||||
|
||||
r7 := ToRawUrlBase64([]string{"7", "5", "9", "4", "23"})
|
||||
d7, _ := base64.RawURLEncoding.DecodeString(r7)
|
||||
assert.Equal("[\"7\",\"5\",\"9\",\"4\",\"23\"]", string(d7))
|
||||
|
||||
r8 := ToRawUrlBase64(nil)
|
||||
d8, _ := base64.RawURLEncoding.DecodeString(r8)
|
||||
assert.Equal("", string(d8))
|
||||
|
||||
ch := make(chan int, 3)
|
||||
ch <- 1
|
||||
ch <- 2
|
||||
r9 := ToRawUrlBase64(ch)
|
||||
d9, _ := base64.RawURLEncoding.DecodeString(r9)
|
||||
assert.Equal("", string(d9))
|
||||
|
||||
r10 := ToRawUrlBase64(io.EOF)
|
||||
d10, _ := base64.RawURLEncoding.DecodeString(r10)
|
||||
assert.Equal("EOF", string(d10))
|
||||
|
||||
r11 := ToRawUrlBase64(errors.New("test"))
|
||||
d11, _ := base64.RawURLEncoding.DecodeString(r11)
|
||||
assert.Equal("test", string(d11))
|
||||
|
||||
typedNil := (*int)(nil)
|
||||
r12 := ToRawUrlBase64(typedNil)
|
||||
d12, _ := base64.RawURLEncoding.DecodeString(r12)
|
||||
assert.Equal("", string(d12))
|
||||
|
||||
type nilInterface interface {
|
||||
}
|
||||
var nI nilInterface = nil
|
||||
d13, _ := base64.RawURLEncoding.DecodeString(ToRawUrlBase64(nI))
|
||||
assert.Equal("", string(d13))
|
||||
|
||||
var p unsafe.Pointer
|
||||
d14, _ := base64.RawURLEncoding.DecodeString(ToRawUrlBase64(p))
|
||||
assert.Equal("", string(d14))
|
||||
|
||||
r15 := ToRawUrlBase64("4+3/4?=")
|
||||
d15, _ := base64.RawURLEncoding.DecodeString(r15)
|
||||
assert.Equal("4+3/4?=", string(d15))
|
||||
}
|
||||
|
||||
@@ -76,6 +76,11 @@ func AesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/IOq_g8_lKZD
|
||||
func AesCbcEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(key)
|
||||
data = pkcs7Padding(data, block.BlockSize())
|
||||
|
||||
@@ -95,6 +100,11 @@ func AesCbcEncrypt(data, key []byte) []byte {
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/IOq_g8_lKZD
|
||||
func AesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(key)
|
||||
|
||||
iv := encrypted[:aes.BlockSize]
|
||||
@@ -111,6 +121,11 @@ func AesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/SpaZO0-5Nsp
|
||||
func AesCtrCrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
block, _ := aes.NewCipher(key)
|
||||
|
||||
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
||||
@@ -126,6 +141,11 @@ func AesCtrCrypt(data, key []byte) []byte {
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/tfkF10B13kH
|
||||
func AesCfbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -148,6 +168,11 @@ func AesCfbEncrypt(data, key []byte) []byte {
|
||||
// len(encrypted) should be great than 16, len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/tfkF10B13kH
|
||||
func AesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
if len(encrypted) < aes.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
}
|
||||
@@ -167,6 +192,11 @@ func AesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/VtHxtkUj-3F
|
||||
func AesOfbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -189,6 +219,11 @@ func AesOfbEncrypt(data, key []byte) []byte {
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// Play: https://go.dev/play/p/VtHxtkUj-3F
|
||||
func AesOfbDecrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 16 && size != 24 && size != 32 {
|
||||
panic("key length shoud be 16 or 24 or 32")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -255,6 +290,11 @@ func DesEcbDecrypt(encrypted, key []byte) []byte {
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/4cC4QvWfe3_1
|
||||
func DesCbcEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
}
|
||||
|
||||
block, _ := des.NewCipher(key)
|
||||
data = pkcs7Padding(data, block.BlockSize())
|
||||
|
||||
@@ -275,6 +315,11 @@ func DesCbcEncrypt(data, key []byte) []byte {
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/4cC4QvWfe3_1
|
||||
func DesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
}
|
||||
|
||||
block, _ := des.NewCipher(key)
|
||||
|
||||
iv := encrypted[:des.BlockSize]
|
||||
@@ -291,6 +336,11 @@ func DesCbcDecrypt(encrypted, key []byte) []byte {
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/9-T6OjKpcdw
|
||||
func DesCtrCrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
}
|
||||
|
||||
block, _ := des.NewCipher(key)
|
||||
|
||||
iv := bytes.Repeat([]byte("1"), block.BlockSize())
|
||||
@@ -306,6 +356,11 @@ func DesCtrCrypt(data, key []byte) []byte {
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
||||
func DesCfbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
}
|
||||
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -327,6 +382,11 @@ func DesCfbEncrypt(data, key []byte) []byte {
|
||||
// len(encrypted) should be great than 16, len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/y-eNxcFBlxL
|
||||
func DesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
}
|
||||
|
||||
block, _ := des.NewCipher(key)
|
||||
if len(encrypted) < des.BlockSize {
|
||||
panic("encrypted data is too short")
|
||||
@@ -341,9 +401,14 @@ func DesCfbDecrypt(encrypted, key []byte) []byte {
|
||||
}
|
||||
|
||||
// DesOfbEncrypt encrypt data with key use DES OFB algorithm
|
||||
// len(key) should be 16, 24 or 32.
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/74KmNadjN1J
|
||||
func DesOfbEncrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
}
|
||||
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -365,6 +430,11 @@ func DesOfbEncrypt(data, key []byte) []byte {
|
||||
// len(key) should be 8.
|
||||
// Play: https://go.dev/play/p/74KmNadjN1J
|
||||
func DesOfbDecrypt(data, key []byte) []byte {
|
||||
size := len(key)
|
||||
if size != 8 {
|
||||
panic("key length shoud be 8")
|
||||
}
|
||||
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -123,6 +123,25 @@ func (hm *HashMap) Iterate(iteratee func(key, value any)) {
|
||||
}
|
||||
}
|
||||
|
||||
// FilterByValue returns a filtered HashMap.
|
||||
// If any value is not matching the perdicate function then it returns nil
|
||||
// otherwise it returns the HashMap with selected values.
|
||||
func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap {
|
||||
var filteredHM *HashMap
|
||||
if hm.size > 0 {
|
||||
for i := 0; i < len(hm.table); i++ {
|
||||
item := hm.table[i]
|
||||
if item != nil && perdicate(item.value) {
|
||||
if filteredHM == nil {
|
||||
filteredHM = NewHashMap()
|
||||
}
|
||||
filteredHM.Put(item.key, item.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredHM
|
||||
}
|
||||
|
||||
// Keys returns a slice of the hashmap's keys (random order)
|
||||
func (hm *HashMap) Keys() []any {
|
||||
keys := make([]any, int(hm.size))
|
||||
@@ -168,6 +187,11 @@ func (hm *HashMap) resize() {
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns current size of Hashmap
|
||||
func (hm *HashMap) Size() uint64 {
|
||||
return hm.size
|
||||
}
|
||||
|
||||
func (hm *HashMap) hash(key any) uint64 {
|
||||
h := fnv.New64a()
|
||||
_, _ = h.Write([]byte(fmt.Sprintf("%v", key)))
|
||||
|
||||
@@ -105,3 +105,24 @@ func TestHashMap_GetOrDefault(t *testing.T) {
|
||||
assert.Equal(1, hm.GetOrDefault("a", 5))
|
||||
assert.Equal(5, hm.GetOrDefault("d", 5))
|
||||
}
|
||||
|
||||
func TestHashMap_FilterByValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestHashMap_FilterByValue")
|
||||
|
||||
hm := NewHashMap()
|
||||
|
||||
hm.Put("a", 1)
|
||||
hm.Put("b", 2)
|
||||
hm.Put("c", 3)
|
||||
hm.Put("d", 4)
|
||||
hm.Put("e", 5)
|
||||
hm.Put("f", 6)
|
||||
|
||||
filteredHM := hm.FilterByValue(func(value any) bool {
|
||||
return value.(int) == 1 || value.(int) == 3
|
||||
})
|
||||
|
||||
assert.Equal(uint64(2), filteredHM.Size())
|
||||
}
|
||||
|
||||
@@ -90,6 +90,35 @@ func lastIndexOf[T any](o T, e []T, start int, end int) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying the
|
||||
// functional predicate f(T) bool
|
||||
// if not found return -1.
|
||||
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int {
|
||||
index := -1
|
||||
data := l.getList()
|
||||
for i := len(data) - 1; i >= 0; i-- {
|
||||
if f(data[i]) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// IndexOfFunc returns the first index satisfying the functional predicate f(v) bool
|
||||
// if not found return -1.
|
||||
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int {
|
||||
index := -1
|
||||
data := l.getList()
|
||||
for i, v := range data {
|
||||
if f(v) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// get returns the element at the specified position in this list.
|
||||
func get[T any](o []T, index int) *T {
|
||||
return &o[index]
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestCopyOnWriteList_ValueOf(t *testing.T) {
|
||||
@@ -233,3 +234,35 @@ func TestCopyOnWriteList_SubList(t *testing.T) {
|
||||
subList = list.SubList(11, 1)
|
||||
assert.Equal([]int{}, subList)
|
||||
}
|
||||
|
||||
func TestCopyOnWriteListIndexOfFunc(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestIndexOfFunc")
|
||||
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3})
|
||||
i := list.IndexOfFunc(func(a int) bool { return a == 1 })
|
||||
assert.Equal(0, i)
|
||||
|
||||
i = list.IndexOfFunc(func(a int) bool { return a == 4 })
|
||||
assert.Equal(-1, i)
|
||||
}
|
||||
|
||||
func TestNewCopyOnWriteListLastIndexOfFunc(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestLastIndexOfFunc")
|
||||
|
||||
list := NewCopyOnWriteList([]int{1, 2, 3, 3, 3, 3, 4, 5, 6, 9})
|
||||
i := list.LastIndexOfFunc(func(a int) bool { return a == 3 })
|
||||
assert.Equal(5, i)
|
||||
|
||||
i = list.LastIndexOfFunc(func(a int) bool { return a == 10 })
|
||||
assert.Equal(-1, i)
|
||||
|
||||
i = list.LastIndexOfFunc(func(a int) bool { return a == 4 })
|
||||
assert.Equal(6, i)
|
||||
|
||||
i = list.LastIndexOfFunc(func(a int) bool { return a == 1 })
|
||||
assert.Equal(0, i)
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ type Optional[T any] struct {
|
||||
mu *sync.RWMutex
|
||||
}
|
||||
|
||||
// Empty returns an empty Optional instance.
|
||||
func Empty[T any]() Optional[T] {
|
||||
// Default returns an default Optional instance.
|
||||
func Default[T any]() Optional[T] {
|
||||
return Optional[T]{mu: &sync.RWMutex{}}
|
||||
}
|
||||
|
||||
@@ -20,29 +20,29 @@ func Of[T any](value T) Optional[T] {
|
||||
return Optional[T]{value: &value, mu: &sync.RWMutex{}}
|
||||
}
|
||||
|
||||
// OfNullable returns an Optional for a given value, which may be nil.
|
||||
func OfNullable[T any](value *T) Optional[T] {
|
||||
// FromNillable returns an Optional for a given value, which may be nil.
|
||||
func FromNillable[T any](value *T) Optional[T] {
|
||||
if value == nil {
|
||||
return Empty[T]()
|
||||
return Default[T]()
|
||||
}
|
||||
return Optional[T]{value: value, mu: &sync.RWMutex{}}
|
||||
}
|
||||
|
||||
// IsPresent checks if there is a value present.
|
||||
func (o Optional[T]) IsPresent() bool {
|
||||
// IsNotNil checks if there is a value present.
|
||||
func (o Optional[T]) IsNotNil() bool {
|
||||
o.mu.RLock()
|
||||
defer o.mu.RUnlock()
|
||||
|
||||
return o.value != nil
|
||||
}
|
||||
|
||||
// IsEmpty checks if the Optional is empty.
|
||||
func (o Optional[T]) IsEmpty() bool {
|
||||
return !o.IsPresent()
|
||||
// IsNil checks if the Optional is nil.
|
||||
func (o Optional[T]) IsNil() bool {
|
||||
return !o.IsNotNil()
|
||||
}
|
||||
|
||||
// IfPresent performs the given action with the value if a value is present.
|
||||
func (o Optional[T]) IfPresent(action func(value T)) {
|
||||
// IfNotNil performs the given action with the value if a value is not nil.
|
||||
func (o Optional[T]) IfNotNil(action func(value T)) {
|
||||
o.mu.RLock()
|
||||
defer o.mu.RUnlock()
|
||||
|
||||
@@ -51,20 +51,20 @@ func (o Optional[T]) IfPresent(action func(value T)) {
|
||||
}
|
||||
}
|
||||
|
||||
// IfPresentOrElse performs the action with the value if present, otherwise performs the empty-based action.
|
||||
func (o Optional[T]) IfPresentOrElse(action func(value T), emptyAction func()) {
|
||||
// IfNotNilOrElse performs the action with the value if present, otherwise performs the fallback action.
|
||||
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func()) {
|
||||
o.mu.RLock()
|
||||
defer o.mu.RUnlock()
|
||||
|
||||
if o.value != nil {
|
||||
action(*o.value)
|
||||
} else {
|
||||
emptyAction()
|
||||
fallbackAction()
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value if present, otherwise panics.
|
||||
func (o Optional[T]) Get() T {
|
||||
// Unwarp returns the value if not nil, otherwise panics.
|
||||
func (o Optional[T]) Unwarp() T {
|
||||
o.mu.RLock()
|
||||
defer o.mu.RUnlock()
|
||||
|
||||
@@ -74,7 +74,7 @@ func (o Optional[T]) Get() T {
|
||||
return *o.value
|
||||
}
|
||||
|
||||
// OrElse returns the value if present, otherwise returns other.
|
||||
// OrElse returns the value if is not nil, otherwise returns other.
|
||||
func (o Optional[T]) OrElse(other T) T {
|
||||
o.mu.RLock()
|
||||
defer o.mu.RUnlock()
|
||||
@@ -85,24 +85,24 @@ func (o Optional[T]) OrElse(other T) T {
|
||||
return other
|
||||
}
|
||||
|
||||
// OrElseGet returns the value if present, otherwise invokes supplier and returns the result.
|
||||
func (o Optional[T]) OrElseGet(supplier func() T) T {
|
||||
// OrElseGet returns the value if is not nil, otherwise invokes action and returns the result.
|
||||
func (o Optional[T]) OrElseGet(action func() T) T {
|
||||
o.mu.RLock()
|
||||
defer o.mu.RUnlock()
|
||||
|
||||
if o.value != nil {
|
||||
return *o.value
|
||||
}
|
||||
return supplier()
|
||||
return action()
|
||||
}
|
||||
|
||||
// OrElseThrow returns the value if present, otherwise returns an error.
|
||||
func (o Optional[T]) OrElseThrow(errorSupplier func() error) (T, error) {
|
||||
// OrElseTrigger returns the value if present, otherwise returns an error.
|
||||
func (o Optional[T]) OrElseTrigger(errorHandler func() error) (T, error) {
|
||||
o.mu.RLock()
|
||||
defer o.mu.RUnlock()
|
||||
|
||||
if o.value == nil {
|
||||
return *new(T), errorSupplier()
|
||||
return *new(T), errorHandler()
|
||||
}
|
||||
return *o.value, nil
|
||||
}
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
func TestDefault(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestEmpty")
|
||||
opt := Empty[int]()
|
||||
opt := Default[int]()
|
||||
|
||||
assert.ShouldBeTrue(opt.IsEmpty())
|
||||
assert.ShouldBeTrue(opt.IsNil())
|
||||
}
|
||||
|
||||
func TestOf(t *testing.T) {
|
||||
@@ -19,30 +19,30 @@ func TestOf(t *testing.T) {
|
||||
value := 42
|
||||
opt := Of(value)
|
||||
|
||||
assert.ShouldBeTrue(opt.IsPresent())
|
||||
assert.Equal(opt.Get(), value)
|
||||
assert.ShouldBeTrue(opt.IsNotNil())
|
||||
assert.Equal(opt.Unwarp(), value)
|
||||
}
|
||||
|
||||
func TestOfNullable(t *testing.T) {
|
||||
func TestFromNillable(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestOfNullable")
|
||||
var value *int = nil
|
||||
opt := OfNullable(value)
|
||||
opt := FromNillable(value)
|
||||
|
||||
assert.ShouldBeFalse(opt.IsPresent())
|
||||
assert.ShouldBeFalse(opt.IsNotNil())
|
||||
|
||||
value = new(int)
|
||||
*value = 42
|
||||
opt = OfNullable(value)
|
||||
opt = FromNillable(value)
|
||||
|
||||
assert.ShouldBeTrue(opt.IsPresent())
|
||||
assert.ShouldBeTrue(opt.IsNotNil())
|
||||
}
|
||||
|
||||
func TestOrElse(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestOrElse")
|
||||
optEmpty := Empty[int]()
|
||||
optDefault := Default[int]()
|
||||
defaultValue := 100
|
||||
|
||||
val := optEmpty.OrElse(defaultValue)
|
||||
val := optDefault.OrElse(defaultValue)
|
||||
assert.Equal(val, defaultValue)
|
||||
|
||||
optWithValue := Of(42)
|
||||
@@ -53,72 +53,72 @@ func TestOrElse(t *testing.T) {
|
||||
func TestOrElseGetHappyPath(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestOrElseGetHappyPath")
|
||||
optWithValue := Of(42)
|
||||
supplier := func() int { return 100 }
|
||||
action := func() int { return 100 }
|
||||
|
||||
val := optWithValue.OrElseGet(supplier)
|
||||
val := optWithValue.OrElseGet(action)
|
||||
assert.Equal(val, 42)
|
||||
}
|
||||
|
||||
func TestOrElseGet(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestOrElseGet")
|
||||
optEmpty := Empty[int]()
|
||||
supplier := func() int { return 100 }
|
||||
optDefault := Default[int]()
|
||||
action := func() int { return 100 }
|
||||
|
||||
val := optEmpty.OrElseGet(supplier)
|
||||
assert.Equal(val, supplier())
|
||||
val := optDefault.OrElseGet(action)
|
||||
assert.Equal(val, action())
|
||||
}
|
||||
|
||||
func TestOrElseThrow(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestOrElseThrow")
|
||||
optEmpty := Empty[int]()
|
||||
_, err := optEmpty.OrElseThrow(func() error { return errors.New("no value") })
|
||||
func TestOrElseTrigger(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "OrElseTrigger")
|
||||
optDefault := Default[int]()
|
||||
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
|
||||
|
||||
assert.Equal(err.Error(), "no value")
|
||||
|
||||
optWithValue := Of(42)
|
||||
val, err := optWithValue.OrElseThrow(func() error { return errors.New("no value") })
|
||||
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(val, 42)
|
||||
}
|
||||
|
||||
func TestIfPresent(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIfPresent")
|
||||
func TestIfNotNil(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "IfNotNil")
|
||||
called := false
|
||||
action := func(value int) { called = true }
|
||||
|
||||
optEmpty := Empty[int]()
|
||||
optEmpty.IfPresent(action)
|
||||
optDefault := Default[int]()
|
||||
optDefault.IfNotNil(action)
|
||||
|
||||
assert.ShouldBeFalse(called)
|
||||
|
||||
called = false // Reset for next test
|
||||
optWithValue := Of(42)
|
||||
optWithValue.IfPresent(action)
|
||||
optWithValue.IfNotNil(action)
|
||||
|
||||
assert.ShouldBeTrue(called)
|
||||
}
|
||||
|
||||
func TestIfPresentOrElse(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIfPresentOrElse")
|
||||
func TestIfNotNilOrElse(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestIfNotNilOrElse")
|
||||
|
||||
// Test when value is present
|
||||
calledWithValue := false
|
||||
valueAction := func(value int) { calledWithValue = true }
|
||||
emptyAction := func() { t.Errorf("Empty action should not be called when value is present") }
|
||||
fallbackAction := func() { t.Errorf("Empty action should not be called when value is present") }
|
||||
|
||||
optWithValue := Of(42)
|
||||
optWithValue.IfPresentOrElse(valueAction, emptyAction)
|
||||
optWithValue.IfNotNilOrElse(valueAction, fallbackAction)
|
||||
|
||||
assert.ShouldBeTrue(calledWithValue)
|
||||
|
||||
// Test when value is not present
|
||||
calledWithEmpty := false
|
||||
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
|
||||
emptyAction = func() { calledWithEmpty = true }
|
||||
fallbackAction = func() { calledWithEmpty = true }
|
||||
|
||||
optEmpty := Empty[int]()
|
||||
optEmpty.IfPresentOrElse(valueAction, emptyAction)
|
||||
optDefault := Default[int]()
|
||||
optDefault.IfNotNilOrElse(valueAction, fallbackAction)
|
||||
|
||||
assert.ShouldBeTrue(calledWithEmpty)
|
||||
}
|
||||
@@ -133,19 +133,19 @@ func TestGetWithPanicStandard(t *testing.T) {
|
||||
r := recover()
|
||||
assert.IsNil(r)
|
||||
}()
|
||||
val := optWithValue.Get()
|
||||
val := optWithValue.Unwarp()
|
||||
if val != 42 {
|
||||
t.Errorf("Expected Get to return 42, got %v", val)
|
||||
t.Errorf("Expected Unwarp to return 42, got %v", val)
|
||||
}
|
||||
}()
|
||||
|
||||
// Test when value is not present
|
||||
optEmpty := Empty[int]()
|
||||
optDefault := Default[int]()
|
||||
func() {
|
||||
defer func() {
|
||||
r := recover()
|
||||
assert.IsNotNil(r)
|
||||
}()
|
||||
_ = optEmpty.Get()
|
||||
_ = optDefault.Unwarp()
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -4,19 +4,21 @@
|
||||
// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate.
|
||||
package datastructure
|
||||
|
||||
import "sort"
|
||||
|
||||
// Set is a data container, like slice, but element of set is not duplicate.
|
||||
type Set[T comparable] map[T]struct{}
|
||||
|
||||
// NewSet return a instance of set
|
||||
func NewSet[T comparable](items ...T) Set[T] {
|
||||
set := make(Set[T])
|
||||
// New create a instance of set from given values.
|
||||
func New[T comparable](items ...T) Set[T] {
|
||||
set := make(Set[T], len(items))
|
||||
set.Add(items...)
|
||||
return set
|
||||
}
|
||||
|
||||
// NewSetFromSlice create a set from slice
|
||||
func NewSetFromSlice[T comparable](items []T) Set[T] {
|
||||
set := make(Set[T])
|
||||
// FromSlice create a set from given slice.
|
||||
func FromSlice[T comparable](items []T) Set[T] {
|
||||
set := make(Set[T], len(items))
|
||||
for _, item := range items {
|
||||
set.Add(item)
|
||||
}
|
||||
@@ -77,8 +79,7 @@ func (s Set[T]) ContainAll(other Set[T]) bool {
|
||||
|
||||
// Clone return a copy of set
|
||||
func (s Set[T]) Clone() Set[T] {
|
||||
set := NewSet[T]()
|
||||
set.Add(s.Values()...)
|
||||
set := FromSlice(s.ToSlice())
|
||||
return set
|
||||
}
|
||||
|
||||
@@ -116,14 +117,11 @@ func (s Set[T]) Size() int {
|
||||
}
|
||||
|
||||
// Values return all values of set
|
||||
// Deprecated: Values function is deprecated and will be removed in future versions. Please use ToSlice() function instead.
|
||||
//
|
||||
// The ToSlice() function provides the same functionality as Values and returns a slice containing all values of the set.
|
||||
func (s Set[T]) Values() []T {
|
||||
result := make([]T, 0, len(s))
|
||||
|
||||
s.Iterate(func(value T) {
|
||||
result = append(result, value)
|
||||
})
|
||||
|
||||
return result
|
||||
return s.ToSlice()
|
||||
}
|
||||
|
||||
// Union creates a new set contain all element of set s and other
|
||||
@@ -135,7 +133,7 @@ func (s Set[T]) Union(other Set[T]) Set[T] {
|
||||
|
||||
// Intersection creates a new set whose element both be contained in set s and other
|
||||
func (s Set[T]) Intersection(other Set[T]) Set[T] {
|
||||
set := NewSet[T]()
|
||||
set := New[T]()
|
||||
s.Iterate(func(value T) {
|
||||
if other.Contain(value) {
|
||||
set.Add(value)
|
||||
@@ -147,7 +145,7 @@ func (s Set[T]) Intersection(other Set[T]) Set[T] {
|
||||
|
||||
// SymmetricDifference creates a new set whose element is in set1 or set2, but not in both sets
|
||||
func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
|
||||
set := NewSet[T]()
|
||||
set := New[T]()
|
||||
s.Iterate(func(value T) {
|
||||
if !other.Contain(value) {
|
||||
set.Add(value)
|
||||
@@ -163,9 +161,9 @@ func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
|
||||
return set
|
||||
}
|
||||
|
||||
// Minus creates an set of whose element in origin set but not in compared set
|
||||
// Minus creates a set of whose element in origin set but not in compared set
|
||||
func (s Set[T]) Minus(comparedSet Set[T]) Set[T] {
|
||||
set := NewSet[T]()
|
||||
set := New[T]()
|
||||
|
||||
s.Iterate(func(value T) {
|
||||
if !comparedSet.Contain(value) {
|
||||
@@ -197,3 +195,25 @@ func (s Set[T]) Pop() (v T, ok bool) {
|
||||
|
||||
return v, false
|
||||
}
|
||||
|
||||
// ToSlice returns a slice containing all values of the set.
|
||||
func (s Set[T]) ToSlice() []T {
|
||||
if s.IsEmpty() {
|
||||
return []T{}
|
||||
}
|
||||
result := make([]T, 0, s.Size())
|
||||
s.Iterate(func(value T) {
|
||||
result = append(result, value)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ToSortedSlice returns a sorted slice containing all values of the set.
|
||||
func (s Set[T]) ToSortedSlice(less func(v1, v2 T) bool) []T {
|
||||
result := s.ToSlice()
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return less(result[i], result[j])
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
func TestSet_NewSetFromSlice(t *testing.T) {
|
||||
func TestSet_FromSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_NewSetFromSlice")
|
||||
assert := internal.NewAssert(t, "TestSet_FromSlice")
|
||||
|
||||
s1 := NewSetFromSlice([]int{1, 2, 2, 3})
|
||||
s1 := FromSlice([]int{1, 2, 2, 3})
|
||||
assert.Equal(3, s1.Size())
|
||||
assert.Equal(true, s1.Contain(1))
|
||||
assert.Equal(true, s1.Contain(2))
|
||||
assert.Equal(true, s1.Contain(3))
|
||||
|
||||
s2 := NewSetFromSlice([]int{})
|
||||
s2 := FromSlice([]int{})
|
||||
assert.Equal(0, s2.Size())
|
||||
}
|
||||
|
||||
@@ -26,10 +28,10 @@ func TestSet_Add(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Add")
|
||||
|
||||
set := NewSet[int]()
|
||||
set := New[int]()
|
||||
set.Add(1, 2, 3)
|
||||
|
||||
cmpSet := NewSet(1, 2, 3)
|
||||
cmpSet := New(1, 2, 3)
|
||||
|
||||
assert.Equal(true, set.Equal(cmpSet))
|
||||
}
|
||||
@@ -39,12 +41,12 @@ func TestSet_AddIfNotExist(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_AddIfNotExist")
|
||||
|
||||
set := NewSet[int]()
|
||||
set := New[int]()
|
||||
set.Add(1, 2, 3)
|
||||
|
||||
assert.Equal(false, set.AddIfNotExist(1))
|
||||
assert.Equal(true, set.AddIfNotExist(4))
|
||||
assert.Equal(NewSet(1, 2, 3, 4), set)
|
||||
assert.Equal(New(1, 2, 3, 4), set)
|
||||
}
|
||||
|
||||
func TestSet_AddIfNotExistBy(t *testing.T) {
|
||||
@@ -52,7 +54,7 @@ func TestSet_AddIfNotExistBy(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_AddIfNotExistBy")
|
||||
|
||||
set := NewSet[int]()
|
||||
set := New[int]()
|
||||
set.Add(1, 2)
|
||||
|
||||
ok := set.AddIfNotExistBy(3, func(val int) bool {
|
||||
@@ -75,7 +77,7 @@ func TestSet_Contain(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Contain")
|
||||
|
||||
set := NewSet[int]()
|
||||
set := New[int]()
|
||||
set.Add(1, 2, 3)
|
||||
|
||||
assert.Equal(true, set.Contain(1))
|
||||
@@ -87,9 +89,9 @@ func TestSet_ContainAll(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_ContainAll")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(1, 2)
|
||||
set3 := NewSet(1, 2, 3, 4)
|
||||
set1 := New(1, 2, 3)
|
||||
set2 := New(1, 2)
|
||||
set3 := New(1, 2, 3, 4)
|
||||
|
||||
assert.Equal(true, set1.ContainAll(set2))
|
||||
assert.Equal(false, set1.ContainAll(set3))
|
||||
@@ -100,7 +102,7 @@ func TestSet_Clone(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Clone")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set1 := New(1, 2, 3)
|
||||
set2 := set1.Clone()
|
||||
|
||||
assert.Equal(true, set1.Size() == set2.Size())
|
||||
@@ -112,11 +114,11 @@ func TestSet_Delete(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Delete")
|
||||
|
||||
set := NewSet[int]()
|
||||
set := New[int]()
|
||||
set.Add(1, 2, 3)
|
||||
set.Delete(3)
|
||||
|
||||
assert.Equal(true, set.Equal(NewSet(1, 2)))
|
||||
assert.Equal(true, set.Equal(New(1, 2)))
|
||||
}
|
||||
|
||||
func TestSet_Equal(t *testing.T) {
|
||||
@@ -124,9 +126,9 @@ func TestSet_Equal(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Equal")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(1, 2, 3)
|
||||
set3 := NewSet(1, 2, 3, 4)
|
||||
set1 := New(1, 2, 3)
|
||||
set2 := New(1, 2, 3)
|
||||
set3 := New(1, 2, 3, 4)
|
||||
|
||||
assert.Equal(true, set1.Equal(set2))
|
||||
assert.Equal(false, set1.Equal(set3))
|
||||
@@ -137,7 +139,7 @@ func TestSet_Iterate(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Iterate")
|
||||
|
||||
set := NewSet(1, 2, 3)
|
||||
set := New(1, 2, 3)
|
||||
arr := []int{}
|
||||
set.Iterate(func(value int) {
|
||||
arr = append(arr, value)
|
||||
@@ -151,7 +153,7 @@ func TestSet_IsEmpty(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_IsEmpty")
|
||||
|
||||
set := NewSet[int]()
|
||||
set := New[int]()
|
||||
assert.Equal(true, set.IsEmpty())
|
||||
}
|
||||
|
||||
@@ -160,7 +162,7 @@ func TestSet_Size(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Size")
|
||||
|
||||
set := NewSet(1, 2, 3)
|
||||
set := New(1, 2, 3)
|
||||
assert.Equal(3, set.Size())
|
||||
}
|
||||
|
||||
@@ -169,7 +171,7 @@ func TestSet_Values(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Values")
|
||||
|
||||
set := NewSet(1, 2, 3)
|
||||
set := New(1, 2, 3)
|
||||
values := set.Values()
|
||||
|
||||
assert.Equal(3, len(values))
|
||||
@@ -180,12 +182,12 @@ func TestSet_Union(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Union")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(2, 3, 4, 5)
|
||||
set1 := New(1, 2, 3)
|
||||
set2 := New(2, 3, 4, 5)
|
||||
|
||||
unionSet := set1.Union(set2)
|
||||
|
||||
assert.Equal(NewSet(1, 2, 3, 4, 5), unionSet)
|
||||
assert.Equal(New(1, 2, 3, 4, 5), unionSet)
|
||||
}
|
||||
|
||||
func TestSet_Intersection(t *testing.T) {
|
||||
@@ -193,11 +195,11 @@ func TestSet_Intersection(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Intersection")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(2, 3, 4, 5)
|
||||
set1 := New(1, 2, 3)
|
||||
set2 := New(2, 3, 4, 5)
|
||||
intersectionSet := set1.Intersection(set2)
|
||||
|
||||
assert.Equal(NewSet(2, 3), intersectionSet)
|
||||
assert.Equal(New(2, 3), intersectionSet)
|
||||
}
|
||||
|
||||
func TestSet_SymmetricDifference(t *testing.T) {
|
||||
@@ -205,10 +207,10 @@ func TestSet_SymmetricDifference(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_SymmetricDifference")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(2, 3, 4, 5)
|
||||
set1 := New(1, 2, 3)
|
||||
set2 := New(2, 3, 4, 5)
|
||||
|
||||
assert.Equal(NewSet(1, 4, 5), set1.SymmetricDifference(set2))
|
||||
assert.Equal(New(1, 4, 5), set1.SymmetricDifference(set2))
|
||||
}
|
||||
|
||||
func TestSet_Minus(t *testing.T) {
|
||||
@@ -216,16 +218,16 @@ func TestSet_Minus(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_Minus")
|
||||
|
||||
set1 := NewSet(1, 2, 3)
|
||||
set2 := NewSet(2, 3, 4, 5)
|
||||
set3 := NewSet(2, 3)
|
||||
set1 := New(1, 2, 3)
|
||||
set2 := New(2, 3, 4, 5)
|
||||
set3 := New(2, 3)
|
||||
|
||||
assert.Equal(NewSet(1), set1.Minus(set2))
|
||||
assert.Equal(NewSet(4, 5), set2.Minus(set3))
|
||||
assert.Equal(New(1), set1.Minus(set2))
|
||||
assert.Equal(New(4, 5), set2.Minus(set3))
|
||||
}
|
||||
|
||||
func TestEachWithBreak(t *testing.T) {
|
||||
// s := NewSet(1, 2, 3, 4, 5)
|
||||
// s := New(1, 2, 3, 4, 5)
|
||||
|
||||
// var sum int
|
||||
|
||||
@@ -244,7 +246,7 @@ func TestEachWithBreak(t *testing.T) {
|
||||
// func TestPop(t *testing.T) {
|
||||
// assert := internal.NewAssert(t, "TestPop")
|
||||
|
||||
// s := NewSet[int]()
|
||||
// s := New[int]()
|
||||
|
||||
// val, ok := s.Pop()
|
||||
// assert.Equal(0, val)
|
||||
@@ -254,9 +256,71 @@ func TestEachWithBreak(t *testing.T) {
|
||||
// s.Add(2)
|
||||
// s.Add(3)
|
||||
|
||||
// // s = NewSet(1, 2, 3, 4, 5)
|
||||
// // s = New(1, 2, 3, 4, 5)
|
||||
|
||||
// val, ok = s.Pop()
|
||||
// assert.Equal(3, val)
|
||||
// assert.Equal(true, ok)
|
||||
// }
|
||||
|
||||
func TestSet_ToSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_ToSlice")
|
||||
|
||||
set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1})
|
||||
set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0})
|
||||
set3 := New[string]()
|
||||
|
||||
slice1 := set1.ToSlice()
|
||||
slice2 := set2.ToSlice()
|
||||
slice3 := set3.ToSlice()
|
||||
|
||||
sort.Ints(slice1)
|
||||
sort.Float64s(slice2)
|
||||
|
||||
assert.Equal(5, len(slice1))
|
||||
assert.Equal(4, len(slice2))
|
||||
assert.Equal(0, len(slice3))
|
||||
|
||||
assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7}))
|
||||
assert.Equal(true, reflect.DeepEqual(slice2, []float64{-2.65, 0, 1.11, 4.25}))
|
||||
assert.Equal("[]string", reflect.TypeOf(slice3).String())
|
||||
}
|
||||
|
||||
func TestSet_ToSortedSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestSet_ToSortedSlice")
|
||||
|
||||
set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1})
|
||||
set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0})
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
set3 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
|
||||
|
||||
slice1 := set1.ToSortedSlice(func(v1, v2 int) bool {
|
||||
return v1 < v2
|
||||
})
|
||||
slice2 := set2.ToSortedSlice(func(v1, v2 float64) bool {
|
||||
return v2 < v1
|
||||
})
|
||||
slice3 := set3.ToSortedSlice(func(v1, v2 Person) bool {
|
||||
return v1.Age < v2.Age
|
||||
})
|
||||
|
||||
assert.Equal(5, len(slice1))
|
||||
assert.Equal(4, len(slice2))
|
||||
assert.Equal(3, len(slice3))
|
||||
|
||||
assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7}))
|
||||
assert.Equal(true, reflect.DeepEqual(slice2, []float64{4.25, 1.11, 0, -2.65}))
|
||||
assert.Equal(true, reflect.DeepEqual(slice3, []Person{
|
||||
{"Jerry", 18},
|
||||
{"Tom", 20},
|
||||
{"Spike", 25},
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@ import (
|
||||
- [ToInterface](#ToInterface)
|
||||
- [Utf8ToGbk](#Utf8ToGbk)
|
||||
- [GbkToUtf8](#GbkToUtf8)
|
||||
- [ToStdBase64](#ToStdBase64)
|
||||
- [ToUrlBase64](#ToUrlBase64)
|
||||
- [ToRawStdBase64](#ToRawStdBase64)
|
||||
- [ToRawUrlBase64](#ToRawUrlBase64)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -876,3 +880,272 @@ func main() {
|
||||
// hello
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToStdBase64">ToStdBase64</span>
|
||||
|
||||
<p>将值转换为StdBase64编码的字符串。error类型的数据也会把error的原因进行编码,复杂的结构会转为JSON格式的字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToStdBase64(value any) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
afterEncode := convertor.ToStdBase64(nil)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
afterEncode = convertor.ToStdBase64("")
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode = convertor.ToStdBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = convertor.ToStdBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = convertor.ToStdBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = convertor.ToStdBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = convertor.ToStdBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = convertor.ToStdBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = convertor.ToStdBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
//
|
||||
//
|
||||
// aGVsbG8=
|
||||
// aGVsbG8=
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||
// MTIzLjQ1Ng==
|
||||
// dHJ1ZQ==
|
||||
// ZXJy
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### <span id="ToUrlBase64">ToUrlBase64</span>
|
||||
|
||||
<p>值转换为 ToUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToUrlBase64(value any) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
afterEncode := convertor.ToUrlBase64(nil)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode = convertor.ToUrlBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = convertor.ToUrlBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = convertor.ToUrlBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = convertor.ToUrlBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = convertor.ToUrlBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = convertor.ToUrlBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = convertor.ToUrlBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
//
|
||||
// aGVsbG8=
|
||||
// aGVsbG8=
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||
// MTIzLjQ1Ng==
|
||||
// dHJ1ZQ==
|
||||
// ZXJy
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### <span id="ToRawStdBase64">ToRawStdBase64</span>
|
||||
|
||||
<p>值转换为 ToRawStdBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToRawStdBase64(value any) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode = convertor.ToRawStdBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = convertor.ToRawStdBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = convertor.ToRawStdBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = convertor.ToRawStdBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = convertor.ToRawStdBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = convertor.ToRawStdBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
// aGVsbG8
|
||||
// aGVsbG8
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||
// MTIzLjQ1Ng
|
||||
// dHJ1ZQ
|
||||
// ZXJy
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToRawUrlBase64">ToRawUrlBase64</span>
|
||||
|
||||
<p>值转换为 ToRawUrlBase64 编码的字符串。error 类型的数据也会把 error 的原因进行编码,复杂的结构会转为 JSON 格式的字符串</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ToRawUrlBase64(value any) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode = convertor.ToRawUrlBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = convertor.ToRawUrlBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = convertor.ToRawUrlBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = convertor.ToRawUrlBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = convertor.ToRawStdBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = convertor.ToRawStdBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
// aGVsbG8
|
||||
// aGVsbG8
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||
// MTIzLjQ1Ng
|
||||
// dHJ1ZQ
|
||||
// ZXJy
|
||||
}
|
||||
```
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
- [Remove](#Remove)
|
||||
- [IndexOf](#IndexOf)
|
||||
- [LastIndexOf](#LastIndexOf)
|
||||
- [IndexOfFunc](#IndexOfFunc)
|
||||
- [LastIndexOfFunc](#LastIndexOfFunc)
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [Contain](#Contain)
|
||||
- [ValueOf](#ValueOf)
|
||||
@@ -198,6 +200,58 @@ func main() {
|
||||
|
||||
```
|
||||
|
||||
### <span id="IndexOfFunc">IndexOfFunc</span>
|
||||
<p>返回第一个满足判断函数f(v)的元素的索引,如果找不到则返回-1。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1, 2, 3})
|
||||
|
||||
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 1 })) //0
|
||||
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 0 })) //-1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LastIndexOfFunc">LastIndexOfFunc</span>
|
||||
<p>返回最后一个满足判断函数f(v)的元素的索引,如果找不到则返回-1。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1, 2, 3, 1})
|
||||
|
||||
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3
|
||||
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1
|
||||
}
|
||||
```
|
||||
|
||||
### IsEmpty
|
||||
|
||||
如果此列表不包含任何元素,则返回 true。
|
||||
|
||||
@@ -22,17 +22,16 @@ import (
|
||||
## 目录
|
||||
|
||||
- [Of](#Of)
|
||||
- [OfNullable](#OfNullable)
|
||||
- [Empty](#Empty)
|
||||
- [IsPresent](#IsPresent)
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [IfPresent](#IfPresent)
|
||||
- [IfPresentOrElse](#IfPresentOrElse)
|
||||
- [Get](#Get)
|
||||
- [FromNillable](#FromNillable)
|
||||
- [Default](#Default)
|
||||
- [IsNotNil](#IsNotNil)
|
||||
- [IsNil](#IsNil)
|
||||
- [IsNotNil](#IsNotNil)
|
||||
- [IfNotNilOrElse](#IfNotNilOrElse)
|
||||
- [Umwarp](#Umwarp)
|
||||
- [OrElse](#OrElse)
|
||||
- [OrElseGet](#OrElseGet)
|
||||
- [OrElseThrow](#OrElseThrow)
|
||||
|
||||
- [OrElseTrigger](#OrElseTrigger)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
@@ -68,14 +67,13 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="OfNullable">OfNullable</span>
|
||||
|
||||
### <span id="FromNillable">FromNillable</span>
|
||||
<p>返回一个包含给定值的Optional,该值可能为空 (nil)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func OfNullable[T any](value *T) Optional[T]
|
||||
func FromNillable[T any](value *T) Optional[T]
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
@@ -89,15 +87,15 @@ import (
|
||||
|
||||
func main() {
|
||||
var value *int = nil
|
||||
opt := optional.OfNullable(value)
|
||||
opt := optional.FromNillable(value)
|
||||
|
||||
fmt.Println(opt.IsPresent())
|
||||
fmt.Println(opt.IsNotNil())
|
||||
|
||||
value = new(int)
|
||||
*value = 42
|
||||
opt = optional.OfNullable(value)
|
||||
opt = optional.FromNillable(value)
|
||||
|
||||
fmt.Println(opt.IsPresent())
|
||||
fmt.Println(opt.IsNotNil())
|
||||
|
||||
|
||||
// Output:
|
||||
@@ -107,14 +105,13 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="Empty">Empty</span>
|
||||
|
||||
### <span id="Default">Default</span>
|
||||
<p>返回一个空Optional实例。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Empty[T any]() Optional[T]
|
||||
func Default[T any]() Optional[T]
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
@@ -127,8 +124,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
optEmpty := OfNullable.Empty[int]()
|
||||
fmt.Println(optEmpty.IsEmpty())
|
||||
optDefault := optional.Default[int]()
|
||||
fmt.Println(optDefault.IsNil())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
@@ -136,14 +133,13 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="IsEmpty">IsEmpty</span>
|
||||
|
||||
### <span id="IsNil">IsNil</span>
|
||||
<p>验证Optional是否为空。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) IsEmpty() bool
|
||||
func (o Optional[T]) IsNil() bool
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
@@ -156,23 +152,21 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
optEmpty := OfNullable.Empty[int]()
|
||||
fmt.Println(optEmpty.IsEmpty())
|
||||
optDefault := optional.Default[int]()
|
||||
fmt.Println(optDefault.IsNil())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="IsPresent">IsPresent</span>
|
||||
|
||||
### <span id="IsNotNil">IsNotNil</span>
|
||||
<p>检查当前Optional内是否存在值。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) IsPresent() bool
|
||||
func (o Optional[T]) IsNotNil() bool
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
@@ -181,20 +175,20 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
optional "github.com/duke-git/lancet/v2/datastructure/optional"
|
||||
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var value *int = nil
|
||||
opt := optional.OfNullable(value)
|
||||
opt := optional.FromNillable(value)
|
||||
|
||||
fmt.Println(opt.IsPresent())
|
||||
fmt.Println(opt.IsNotNil())
|
||||
|
||||
value = new(int)
|
||||
*value = 42
|
||||
opt = optional.OfNullable(value)
|
||||
opt = optional.FromNillable(value)
|
||||
|
||||
fmt.Println(opt.IsPresent())
|
||||
fmt.Println(opt.IsNotNil())
|
||||
|
||||
|
||||
// Output:
|
||||
@@ -203,15 +197,13 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="IfPresent">IfPresent</span>
|
||||
|
||||
### <span id="IfNotNil">IfNotNil</span>
|
||||
<p>如果值存在,则使用action方法执行给定的操作。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) IfPresent(action func(value T))
|
||||
func (o Optional[T]) IfNotNil(action func(value T))
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
@@ -220,23 +212,23 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
optional "github.com/duke-git/lancet/v2/datastructure/optional"
|
||||
"github.com/duke-git/lancet/v2/datastructure/optional"
|
||||
)
|
||||
|
||||
func main() {
|
||||
called := false
|
||||
action := func(value int) { called = true }
|
||||
|
||||
optEmpty := optional.Empty[int]()
|
||||
optEmpty.IfPresent(action)
|
||||
optDefault := optional.Default[int]()
|
||||
optDefault.IfNotNil(action)
|
||||
|
||||
fmt.Println(called)
|
||||
|
||||
called = false // Reset for next test
|
||||
optWithValue := optional.Of(42)
|
||||
optWithValue.IfPresent(action)
|
||||
optWithValue.IfNotNil(action)
|
||||
|
||||
fmt.Println(optWithValue.IsPresent())
|
||||
fmt.Println(optWithValue.IsNotNil())
|
||||
|
||||
// Output:
|
||||
// false
|
||||
@@ -245,14 +237,13 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="IfPresentOrElse">IfPresentOrElse</span>
|
||||
|
||||
### <span id="IfNotNilOrElse">IfNotNilOrElse</span>
|
||||
<p>根据是否存在值执行相应的操作:有值则执行指定操作,没有值则执行默认操作。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) IfPresentOrElse(action func(value T), emptyAction func())
|
||||
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func())
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
@@ -270,7 +261,7 @@ func main() {
|
||||
emptyAction := func() { t.Errorf("Empty action should not be called when value is present") }
|
||||
|
||||
optWithValue := optional.Of(42)
|
||||
optWithValue.IfPresentOrElse(valueAction, emptyAction)
|
||||
optWithValue.IfNotNilOrElse(valueAction, emptyAction)
|
||||
|
||||
fmt.Println(calledWithValue)
|
||||
|
||||
@@ -278,8 +269,8 @@ func main() {
|
||||
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
|
||||
emptyAction = func() { calledWithEmpty = true }
|
||||
|
||||
optEmpty := optional.Empty[int]()
|
||||
optEmpty.IfPresentOrElse(valueAction, emptyAction)
|
||||
optDefault := optional.Default[int]()
|
||||
optDefault.IfNotNilOrElse(valueAction, emptyAction)
|
||||
|
||||
fmt.Println(calledWithEmpty)
|
||||
|
||||
@@ -289,13 +280,13 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Get">Get</span>
|
||||
### <span id="Unwrap">Unwrap</span>
|
||||
<p>如果存在,返回该值,否则引发panic。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) Get() T
|
||||
func (o Optional[T]) Unwrap() T
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
@@ -311,7 +302,7 @@ func main() {
|
||||
value := 42
|
||||
opt := optional.Of(value)
|
||||
|
||||
fmt.Println(opt.Get())
|
||||
fmt.Println(opt.Unwrap())
|
||||
|
||||
// Output:
|
||||
// 42
|
||||
@@ -338,8 +329,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
optEmpty := optional.Empty[int]()
|
||||
val := optEmpty.OrElse(100)
|
||||
optDefault := optional.Empty[int]()
|
||||
val := optDefault.OrElse(100)
|
||||
fmt.Println(val)
|
||||
|
||||
optWithValue := optional.Of(42)
|
||||
@@ -359,7 +350,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) OrElseGet(supplier func() T) T
|
||||
func (o Optional[T]) OrElseGet(action func() T) T
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
@@ -372,10 +363,10 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
optEmpty := optional.Empty[int]()
|
||||
supplier := func() int { return 100 }
|
||||
optDefault := optional.Default[int]()
|
||||
action := func() int { return 100 }
|
||||
|
||||
val := optEmpty.OrElseGet(supplier)
|
||||
val := optDefault.OrElseGet(action)
|
||||
fmt.Println(val)
|
||||
|
||||
// Output:
|
||||
@@ -383,14 +374,13 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="OrElseThrow">OrElseThrow</span>
|
||||
### <span id="OrElseTrigger">OrElseTrigger</span>
|
||||
<p>检查Optional值是否存在,如果存在,则直接返回该值,否则返回错误。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) OrElseThrow(errorSupplier func() error) (T, error)
|
||||
OrElseTrigger(errorHandler func() error) (T, error)
|
||||
```
|
||||
<b>示例:</b>
|
||||
|
||||
@@ -403,13 +393,13 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
optEmpty := optional.Empty[int]()
|
||||
_, err := optEmpty.OrElseThrow(func() error { return errors.New("no value") })
|
||||
optDefault := optional.Default[int]()
|
||||
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
|
||||
|
||||
fmt.Println(err.Error())
|
||||
|
||||
optWithValue := optional.Of(42)
|
||||
val, err := optWithValue.OrElseThrow(func() error { return errors.New("no value") })
|
||||
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
|
||||
|
||||
fmt.Println(val)
|
||||
fmt.Println(err)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Set
|
||||
|
||||
Set 集合数据结构,类似列表。Set 中元素不重复。
|
||||
集合数据结构,类似列表。Set中元素不重复。
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
|
||||
## 目录
|
||||
|
||||
- [NewSet](#NewSet)
|
||||
- [NewSetFromSlice](#NewSetFromSlice)
|
||||
- [New](#New)
|
||||
- [FromSlice](#FromSlice)
|
||||
- [Values](#Values)
|
||||
- [Add](#Add)
|
||||
- [AddIfNotExist](#AddIfNotExist)
|
||||
@@ -40,20 +40,23 @@ import (
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
- [Pop](#Pop)
|
||||
- [ToSlice](#ToSlice)
|
||||
- [ToSortedSlice](#ToSortedSlice)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## 文档
|
||||
|
||||
### <span id="NewSet">NewSet</span>
|
||||
### <span id="New">New</span>
|
||||
|
||||
<p>返回Set结构体对象</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type Set[T comparable] map[T]bool
|
||||
func NewSet[T comparable](items ...T) Set[T]
|
||||
type Set[T comparable] map[T]struct{}
|
||||
func New[T comparable](items ...T) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
@@ -67,19 +70,19 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int](1,2,2,3)
|
||||
st := set.New[int](1,2,2,3)
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="NewSetFromSlice">NewSetFromSlice</span>
|
||||
### <span id="FromSlice">FromSlice</span>
|
||||
|
||||
<p>基于切片创建集合</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func NewSetFromSlice[T comparable](items []T) Set[T]
|
||||
func FromSlice[T comparable](items []T) Set[T]
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
@@ -93,14 +96,15 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
|
||||
st := set.FromSlice([]int{1, 2, 2, 3})
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Values">Values</span>
|
||||
### <span id="Values">Values<sup>deprecated</sup></span>
|
||||
|
||||
<p>获取集合中所有元素的切片</p>
|
||||
<p>获取集合中所有元素的切片<br>
|
||||
<a href='#ToSlice'>ToSlice()</a> 方法提供与 Values 方法相同的功能</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
@@ -119,7 +123,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int](1,2,2,3)
|
||||
st := set.New[int](1,2,2,3)
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
@@ -145,7 +149,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st := set.New[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
@@ -173,7 +177,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st := set.New[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
r1 := st.AddIfNotExist(1)
|
||||
@@ -206,7 +210,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st := set.New[int]()
|
||||
st.Add(1, 2)
|
||||
|
||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||
@@ -245,7 +249,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st := set.New[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
set.Delete(3)
|
||||
@@ -274,7 +278,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st := set.New[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
fmt.Println(st.Contain(1)) //true
|
||||
@@ -303,9 +307,9 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(1, 2)
|
||||
set3 := set.NewSet(1, 2, 3, 4)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(1, 2)
|
||||
set3 := set.New(1, 2, 3, 4)
|
||||
|
||||
fmt.Println(set1.ContainAll(set2)) //true
|
||||
fmt.Println(set1.ContainAll(set3)) //false
|
||||
@@ -333,7 +337,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set1 := set.New(1, 2, 3)
|
||||
|
||||
fmt.Println(set1.Size()) //3
|
||||
}
|
||||
@@ -360,7 +364,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set1.Clone()
|
||||
|
||||
fmt.Println(set1.Size() == set2.Size()) //true
|
||||
@@ -389,9 +393,9 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(1, 2, 3)
|
||||
set3 := set.NewSet(1, 2, 3, 4)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(1, 2, 3)
|
||||
set3 := set.New(1, 2, 3, 4)
|
||||
|
||||
fmt.Println(set1.Equal(set2)) //true
|
||||
fmt.Println(set1.Equal(set3)) //false
|
||||
@@ -419,7 +423,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set1 := set.New(1, 2, 3)
|
||||
arr := []int{}
|
||||
set.Iterate(func(item int) {
|
||||
arr = append(arr, item)
|
||||
@@ -450,7 +454,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := set.NewSet(1, 2, 3, 4, 5)
|
||||
s := set.New(1, 2, 3, 4, 5)
|
||||
|
||||
var sum int
|
||||
|
||||
@@ -487,8 +491,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet()
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New()
|
||||
|
||||
fmt.Println(set1.IsEmpty()) //false
|
||||
fmt.Println(set2.IsEmpty()) //true
|
||||
@@ -516,8 +520,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(2, 3, 4, 5)
|
||||
set3 := set1.Union(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //1,2,3,4,5
|
||||
@@ -545,8 +549,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(2, 3, 4, 5)
|
||||
set3 := set1.Intersection(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //2,3
|
||||
@@ -574,8 +578,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(2, 3, 4, 5)
|
||||
set3 := set1.SymmetricDifference(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //1,4,5
|
||||
@@ -603,9 +607,9 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set.NewSet(2, 3)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(2, 3, 4, 5)
|
||||
set3 := set.New(2, 3)
|
||||
|
||||
res1 := set1.Minus(set2)
|
||||
fmt.Println(res1.Values()) //1
|
||||
@@ -636,7 +640,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := set.NewSet[int]()
|
||||
s := set.New[int]()
|
||||
s.Add(1)
|
||||
s.Add(2)
|
||||
s.Add(3)
|
||||
@@ -647,3 +651,58 @@ func main() {
|
||||
fmt.Println(ok) // true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToSlice">ToSlice</span>
|
||||
|
||||
<p>以切片的形式返回集合中所有的元素(无序)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) ToSlice() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
s := set.New(1, 2, 3, 4, 5)
|
||||
|
||||
val := s.ToSlice()
|
||||
fmt.Println(val) // [2 3 4 5 1]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToSortedSlice">ToSortedSlice</span>
|
||||
|
||||
<p>以切片的形式返回集合中所有的元素(按给定的规则排序)</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) ToSortedSlice() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
s1 := set.New(1, 2, 3, 4, 5)
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
|
||||
|
||||
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
|
||||
return v1 < v2
|
||||
})
|
||||
|
||||
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
|
||||
return v1.Age < v2.Age
|
||||
})
|
||||
|
||||
fmt.Println(res1) // [1 2 3 4 5]
|
||||
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
- [CreateFile](#CreateFile)
|
||||
- [CreateDir](#CreateDir)
|
||||
- [CopyFile](#CopyFile)
|
||||
- [CopyDir](#CopyDir)
|
||||
- [CurrentPath](#CurrentPath)
|
||||
- [FileMode](#FileMode)
|
||||
- [MiMeType](#MiMeType)
|
||||
@@ -49,6 +50,8 @@ import (
|
||||
- [WriteStringToFile](#WriteStringToFile)
|
||||
- [WriteBytesToFile](#WriteBytesToFile)
|
||||
- [ReadFile](#ReadFile)
|
||||
- [ChunkRead](#ChunkRead)
|
||||
- [ParallelChunkRead](#ParallelChunkRead)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -162,6 +165,34 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CopyDir">CopyDir</span>
|
||||
|
||||
<p>拷贝文件夹到目标路径,会递归复制文件夹下所有的文件及文件夹,并且访问权限也与源文件夹保持一致。当dstPath存在时会返回error</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CopyDir(srcPath string, dstPath string) error
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/YAyFTA_UuPb)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := fileutil.CopyFile("./test_src", "./test_dest")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CurrentPath">CurrentPath</span>
|
||||
|
||||
<p>返回当前位置的绝对路径。</p>
|
||||
@@ -926,9 +957,123 @@ func main() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(dat))
|
||||
|
||||
// Output:
|
||||
// User-agent: *
|
||||
// Disallow: /deny
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ChunkRead">ChunkRead</span>
|
||||
|
||||
<p>从文件的指定偏移读取块并返回块内所有行。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const mb = 1024 * 1024
|
||||
const defaultChunkSizeMB = 100
|
||||
|
||||
// test1.csv file content:
|
||||
// Lili,22,female
|
||||
// Jim,21,male
|
||||
filePath := "./testdata/test1.csv" // 替换为你的文件路径
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 0, defaultChunkSizeMB*mb)
|
||||
},
|
||||
}
|
||||
|
||||
lines, err := fileutil.ChunkRead(f, 0, 100, &bufPool)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(lines[0])
|
||||
fmt.Println(lines[1])
|
||||
|
||||
// Output:
|
||||
// Lili,22,female
|
||||
// Jim,21,male
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ParallelChunkRead">ParallelChunkRead</span>
|
||||
|
||||
<p>并行读取文件并将每个块的行发送到指定通道。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
// filePath:文件路径
|
||||
// chunkSizeMB: 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整
|
||||
// maxGoroutine: 并发读取分块的数量,设置为0时使用CPU核心数
|
||||
// linesCh: 用于接收返回结果的通道。
|
||||
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const mb = 1024 * 1024
|
||||
const defaultChunkSizeMB = 100 // 默认值
|
||||
|
||||
numParsers := runtime.NumCPU()
|
||||
|
||||
linesCh := make(chan []string, numParsers)
|
||||
|
||||
// test1.csv file content:
|
||||
// Lili,22,female
|
||||
// Jim,21,male
|
||||
filePath := "./testdata/test1.csv"
|
||||
|
||||
go fileutil.ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
|
||||
|
||||
var totalLines int
|
||||
for lines := range linesCh {
|
||||
totalLines += len(lines)
|
||||
|
||||
for _, line := range lines {
|
||||
fmt.Println(line)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(totalLines)
|
||||
|
||||
// Output:
|
||||
// Lili,22,female
|
||||
// Jim,21,male
|
||||
// 2
|
||||
}
|
||||
```
|
||||
@@ -37,6 +37,10 @@ import (
|
||||
- [Or](#Or)
|
||||
- [Negate](#Negate)
|
||||
- [Nor](#Nor)
|
||||
- [Xnor](#Xnor)
|
||||
- [Nand](#Nand)
|
||||
- [AcceptIf](#AcceptIf)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -563,3 +567,128 @@ func main() {
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Nand">Nand</span>
|
||||
|
||||
<p>返回一个复合谓词函数,表示给定谓词函数列表的逻辑非与 (NAND)。仅当列表中所有函数对给定参数返回false时,才返回true,否则返回false。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Nand[T any](predicates ...func(T) bool) func(T) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isNumericAndLength5 := function.Nand(
|
||||
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||
func(s string) bool { return len(s) == 5 },
|
||||
)
|
||||
|
||||
fmt.Println(isNumericAndLength5("12345"))
|
||||
fmt.Println(isNumericAndLength5("1234"))
|
||||
fmt.Println(isNumericAndLength5("abcdef"))
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Xnor">Xnor</span>
|
||||
|
||||
<p>返回一个复合谓词函数,表示给定一组谓词函数的逻辑异或 (XNOR)。只有当所有 谓词函数对给参数都返回true或false时,该谓词函数才返回true。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Xnor[T any](predicates ...func(T) bool) func(T) bool
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isEven := func(i int) bool { return i%2 == 0 }
|
||||
isPositive := func(i int) bool { return i > 0 }
|
||||
|
||||
match := function.Xnor(isEven, isPositive)
|
||||
|
||||
fmt.Println(match(2))
|
||||
fmt.Println(match(-3))
|
||||
fmt.Println(match(3))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="AcceptIf">AcceptIf</span>
|
||||
|
||||
<p>AcceptIf函数会返回另一个函数,该函数的签名与 apply 函数相同,但同时还会包含一个布尔值来表示成功或失败。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
adder := AcceptIf(
|
||||
And(
|
||||
func(x int) bool {
|
||||
return x > 10
|
||||
}, func(x int) bool {
|
||||
return x%2 == 0
|
||||
}),
|
||||
func(x int) int {
|
||||
return x + 1
|
||||
},
|
||||
)
|
||||
|
||||
result, ok := adder(20)
|
||||
fmt.Println(result)
|
||||
fmt.Println(ok)
|
||||
|
||||
result, ok = adder(21)
|
||||
fmt.Println(result)
|
||||
fmt.Println(ok)
|
||||
|
||||
// Output:
|
||||
// 21
|
||||
// true
|
||||
// 0
|
||||
// false
|
||||
}
|
||||
|
||||
```
|
||||
@@ -34,6 +34,10 @@ import (
|
||||
- [RoundToFloat](#RoundToFloat)
|
||||
- [RoundToString](#RoundToString)
|
||||
- [TruncRound](#TruncRound)
|
||||
- [CeilToFloat](#CeilToFloat)
|
||||
- [CeilToString](#CeilToString)
|
||||
- [FloorToFloat](#FloorToFloat)
|
||||
- [FloorToString](#FloorToString)
|
||||
- [Range](#Range)
|
||||
- [RangeWithStep](#RangeWithStep)
|
||||
- [AngleToRadian](#AngleToRadian)
|
||||
@@ -47,6 +51,7 @@ import (
|
||||
- [Log](#Log)
|
||||
- [Sum](#Sum)
|
||||
- [Abs](#Abs)
|
||||
- [Div](#Div)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -392,7 +397,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RoundToFloat(x float64, n int) float64
|
||||
func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ghyb528JRJL)</span></b>
|
||||
@@ -428,7 +433,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RoundToString(x float64, n int) string
|
||||
func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/kZwpBRAcllO)</span></b>
|
||||
@@ -464,7 +469,7 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func TruncRound(x float64, n int) float64
|
||||
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/aumarSHIGzP)</span></b>
|
||||
@@ -493,6 +498,150 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CeilToFloat">CeilToFloat</span>
|
||||
|
||||
<p>向上舍入(进一法),保留n位小数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.CeilToFloat(3.14159, 1)
|
||||
result2 := mathutil.CeilToFloat(3.14159, 2)
|
||||
result3 := mathutil.CeilToFloat(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.2
|
||||
// 3.15
|
||||
// 5
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CeilToString">CeilToString</span>
|
||||
|
||||
<p>向上舍入(进一法),保留n位小数,返回字符串。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.CeilToString(3.14159, 1)
|
||||
result2 := mathutil.CeilToString(3.14159, 2)
|
||||
result3 := mathutil.CeilToString(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.2
|
||||
// 3.15
|
||||
// 5.0000
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FloorToFloat">FloorToFloat</span>
|
||||
|
||||
<p>向下舍入(去尾法),保留n位小数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.FloorToFloat(3.14159, 1)
|
||||
result2 := mathutil.FloorToFloat(3.14159, 2)
|
||||
result3 := mathutil.FloorToFloat(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.1
|
||||
// 3.14
|
||||
// 5
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FloorToString">FloorToString</span>
|
||||
|
||||
<p>向下舍入(去尾法),保留n位小数,返回字符串。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.FloorToString(3.14159, 1)
|
||||
result2 := mathutil.FloorToString(3.14159, 2)
|
||||
result3 := mathutil.FloorToString(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.1
|
||||
// 3.14
|
||||
// 5.0000
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Range">Range</span>
|
||||
|
||||
<p>根据指定的起始值和数量,创建一个数字切片。</p>
|
||||
@@ -965,16 +1114,51 @@ import (
|
||||
|
||||
func main() {
|
||||
result1 := Abs(-1)
|
||||
result2 := Abs(-0.1)
|
||||
result3 := Abs(float32(0.2))
|
||||
result2 := Abs(-0.1)
|
||||
result3 := Abs(float32(0.2))
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 0.1
|
||||
// 0.2
|
||||
// Output:
|
||||
// 1
|
||||
// 0.1
|
||||
// 0.2
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Div">Div</span>
|
||||
|
||||
<p>除法运算.</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.Div(9, 4)
|
||||
result2 := mathutil.Div(1, 2)
|
||||
result3 := mathutil.Div(0, 666)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
// Output:
|
||||
// 2.25
|
||||
// 0.5
|
||||
// 0
|
||||
}
|
||||
```
|
||||
@@ -27,6 +27,10 @@ import (
|
||||
- [RetryFunc](#RetryFunc)
|
||||
- [RetryDuration](#RetryDuration)
|
||||
- [RetryTimes](#RetryTimes)
|
||||
- [BackoffStrategy](#BackoffStrategy)
|
||||
- [RetryWithCustomBackoff](#RetryWithCustomBackoff)
|
||||
- [RetryWithLinearBackoff](#RetryWithLinearBackoff)
|
||||
- [RetryWithExponentialWithJitterBackoff](#RetryWithExponentialWithJitterBackoff)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -260,3 +264,201 @@ func main() {
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="BackoffStrategy">BackoffStrategy</span>
|
||||
|
||||
<p>定义计算退避间隔的方法的接口。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
// BackoffStrategy is an interface that defines a method for calculating backoff intervals.
|
||||
type BackoffStrategy interface {
|
||||
// CalculateInterval returns the time.Duration after which the next retry attempt should be made.
|
||||
CalculateInterval() time.Duration
|
||||
}
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/v2/retry"
|
||||
)
|
||||
|
||||
type ExampleCustomBackoffStrategy struct {
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
|
||||
return c.interval + 1
|
||||
}
|
||||
|
||||
func main() {
|
||||
number := 0
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == 3 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&示例CustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(number)
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RetryWithCustomBackoff">RetryWithCustomBackoff</span>
|
||||
|
||||
<p>设置自定义退避策略。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/v2/retry"
|
||||
)
|
||||
|
||||
type ExampleCustomBackoffStrategy struct {
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
|
||||
return c.interval + 1
|
||||
}
|
||||
|
||||
func main() {
|
||||
number := 0
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == 3 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&示例CustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(number)
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="RetryWithLinearBackoff">RetryWithLinearBackoff</span>
|
||||
|
||||
<p>设置线性策略退避。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RetryWithLinearBackoff(interval time.Duration) Option
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/v2/retry"
|
||||
)
|
||||
|
||||
func main() {
|
||||
number := 0
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == 3 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := retry.Retry(increaseNumber, retry.RetryWithLinearBackoff(time.Microsecond*50))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(number)
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="RetryWithExponentialWithJitterBackoff">RetryWithExponentialWithJitterBackoff</span>
|
||||
|
||||
<p>设置指数策略退避。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/v2/retry"
|
||||
)
|
||||
|
||||
func main() {
|
||||
number := 0
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == 3 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := retry.Retry(increaseNumber, retry.RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(number)
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
@@ -93,6 +93,7 @@ import (
|
||||
- [KeyBy](#KeyBy)
|
||||
- [Join](#Join)
|
||||
- [Partition](#Partition)
|
||||
- [SetToDefaultIf](#SetToDefaultIf)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -2568,4 +2569,35 @@ func main() {
|
||||
// Output:
|
||||
// okk
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="SetToDefaultIf">SetToDefaultIf</span>
|
||||
|
||||
<p>根据给定给定的predicate判定函数来修改切片中的元素。对于满足的元素,将其替换为指定的默认值,同时保持元素在切片中的位置不变。函数返回修改后的切片以及被修改的元素个数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||
|
||||
fmt.Println(modifiedStrs)
|
||||
fmt.Println(count)
|
||||
|
||||
// Output:
|
||||
// [ b c d ]
|
||||
// 3
|
||||
}
|
||||
```
|
||||
@@ -61,6 +61,7 @@ import (
|
||||
- [ContainsAny](#ContainsAny)
|
||||
- [RemoveWhiteSpace](#RemoveWhiteSpace)
|
||||
- [SubInBetween](#SubInBetween)
|
||||
- [HammingDistance](#HammingDistance)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -1495,4 +1496,36 @@ func main() {
|
||||
// abc
|
||||
// bc
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HammingDistance">HammingDistance</span>
|
||||
|
||||
<p>计算两个字符串之间的汉明距离。汉明距离是指对应符号不同的位置数。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
HammingDistance(a, b string) (int, error)
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
result1, _ := strutil.HammingDistance("de", "de")
|
||||
result2, _ := strutil.HammingDistance("a", "d")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 1
|
||||
}
|
||||
```
|
||||
@@ -43,6 +43,10 @@ import (
|
||||
- [ToInterface](#ToInterface)
|
||||
- [Utf8ToGbk](#Utf8ToGbk)
|
||||
- [GbkToUtf8](#GbkToUtf8)
|
||||
- [ToStdBase64](#ToStdBase64)
|
||||
- [ToUrlBase64](#ToUrlBase64)
|
||||
- [ToRawStdBase64](#ToRawStdBase64)
|
||||
- [ToRawUrlBase64](#ToRawUrlBase64)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -606,7 +610,7 @@ func main() {
|
||||
func DecodeByte(data []byte, target any) error
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zI6xsmuQRbn)</span></b>
|
||||
<b>Example:<span style="float:right;display:inline-block;"></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -875,4 +879,273 @@ func main() {
|
||||
// true
|
||||
// hello
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToStdBase64">ToStdBase64</span>
|
||||
|
||||
<p>Convert a value to a string encoded in standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ToStdBase64(value any) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
afterEncode := convertor.ToStdBase64(nil)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
afterEncode = convertor.ToStdBase64("")
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode = convertor.ToStdBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = convertor.ToStdBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = convertor.ToStdBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = convertor.ToStdBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = convertor.ToStdBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = convertor.ToStdBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = convertor.ToStdBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
//
|
||||
//
|
||||
// aGVsbG8=
|
||||
// aGVsbG8=
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||
// MTIzLjQ1Ng==
|
||||
// dHJ1ZQ==
|
||||
// ZXJy
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### <span id="ToUrlBase64">ToUrlBase64</span>
|
||||
|
||||
<p>Convert a value to a string encoded in url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ToUrlBase64(value any) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
afterEncode := convertor.ToUrlBase64(nil)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode = convertor.ToUrlBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = convertor.ToUrlBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = convertor.ToUrlBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = convertor.ToUrlBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = convertor.ToUrlBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = convertor.ToUrlBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = convertor.ToUrlBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
//
|
||||
// aGVsbG8=
|
||||
// aGVsbG8=
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ==
|
||||
// MTIzLjQ1Ng==
|
||||
// dHJ1ZQ==
|
||||
// ZXJy
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### <span id="ToRawStdBase64">ToRawStdBase64</span>
|
||||
|
||||
<p>Convert a value to a string encoded in raw standard Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ToRawStdBase64(value any) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode = convertor.ToRawStdBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = convertor.ToRawStdBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = convertor.ToRawStdBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = convertor.ToRawStdBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = convertor.ToRawStdBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = convertor.ToRawStdBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = convertor.ToRawStdBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
// aGVsbG8
|
||||
// aGVsbG8
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||
// MTIzLjQ1Ng
|
||||
// dHJ1ZQ
|
||||
// ZXJy
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToRawUrlBase64">ToRawUrlBase64</span>
|
||||
|
||||
<p> Convert a value to a string encoded in raw url Base64. Error data of type "error" will also be encoded, and complex structures will be converted to a JSON-formatted string.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func ToRawUrlBase64(value any) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
stringVal := "hello"
|
||||
afterEncode = convertor.ToRawUrlBase64(stringVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
byteSliceVal := []byte("hello")
|
||||
afterEncode = convertor.ToRawUrlBase64(byteSliceVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
intVal := 123
|
||||
afterEncode = convertor.ToRawUrlBase64(intVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
mapVal := map[string]any{"a": "hi", "b": 2, "c": struct {
|
||||
A string
|
||||
B int
|
||||
}{"hello", 3}}
|
||||
afterEncode = convertor.ToRawUrlBase64(mapVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
floatVal := 123.456
|
||||
afterEncode = convertor.ToRawUrlBase64(floatVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
boolVal := true
|
||||
afterEncode = convertor.ToRawStdBase64(boolVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
errVal := errors.New("err")
|
||||
afterEncode = convertor.ToRawStdBase64(errVal)
|
||||
fmt.Println(afterEncode)
|
||||
|
||||
// Output:
|
||||
// aGVsbG8
|
||||
// aGVsbG8
|
||||
// MTIz
|
||||
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
|
||||
// MTIzLjQ1Ng
|
||||
// dHJ1ZQ
|
||||
// ZXJy
|
||||
}
|
||||
```
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
- [Remove](#Remove)
|
||||
- [IndexOf](#IndexOf)
|
||||
- [LastIndexOf](#LastIndexOf)
|
||||
- [IndexOfFunc](#IndexOfFunc)
|
||||
- [LastIndexOfFunc](#LastIndexOfFunc)
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [Contain](#Contain)
|
||||
- [ValueOf](#ValueOf)
|
||||
@@ -197,6 +199,59 @@ func main() {
|
||||
|
||||
```
|
||||
|
||||
|
||||
### <span id="IndexOfFunc">IndexOfFunc</span>
|
||||
<p> IndexOfFunc returns the first index satisfying the functional predicate f(v) bool. if not found return -1.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1, 2, 3})
|
||||
|
||||
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 1 })) //0
|
||||
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 0 })) //-1
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="LastIndexOfFunc">LastIndexOfFunc</span>
|
||||
<p>LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying the functional predicate f(T) bool. if not found return -1.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datastructure/list"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := list.NewCopyOnWriteList([]int{1, 2, 3, 1})
|
||||
|
||||
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3
|
||||
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1
|
||||
}
|
||||
```
|
||||
|
||||
### IsEmpty
|
||||
|
||||
Returns true if this list does not contain any elements.
|
||||
|
||||
@@ -22,16 +22,16 @@ import (
|
||||
## Index
|
||||
|
||||
- [Of](#Of)
|
||||
- [OfNullable](#OfNullable)
|
||||
- [Empty](#Empty)
|
||||
- [IsPresent](#IsPresent)
|
||||
- [IsEmpty](#IsEmpty)
|
||||
- [IfPresent](#IfPresent)
|
||||
- [IfPresentOrElse](#IfPresentOrElse)
|
||||
- [Get](#Get)
|
||||
- [FromNillable](#FromNillable)
|
||||
- [Default](#Default)
|
||||
- [IsNotNil](#IsNotNil)
|
||||
- [IsNil](#IsNil)
|
||||
- [IsNotNil](#IsNotNil)
|
||||
- [IfNotNilOrElse](#IfNotNilOrElse)
|
||||
- [Umwarp](#Umwarp)
|
||||
- [OrElse](#OrElse)
|
||||
- [OrElseGet](#OrElseGet)
|
||||
- [OrElseThrow](#OrElseThrow)
|
||||
- [OrElseTrigger](#OrElseTrigger)
|
||||
|
||||
|
||||
|
||||
@@ -68,13 +68,13 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="OfNullable">OfNullable</span>
|
||||
### <span id="FromNillable">FromNillable</span>
|
||||
<p>Returns an Optional for a given value, which may be nil.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func OfNullable[T any](value *T) Optional[T]
|
||||
func FromNillable[T any](value *T) Optional[T]
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -88,15 +88,15 @@ import (
|
||||
|
||||
func main() {
|
||||
var value *int = nil
|
||||
opt := optional.OfNullable(value)
|
||||
opt := optional.FromNillable(value)
|
||||
|
||||
fmt.Println(opt.IsPresent())
|
||||
fmt.Println(opt.IsNotNil())
|
||||
|
||||
value = new(int)
|
||||
*value = 42
|
||||
opt = optional.OfNullable(value)
|
||||
opt = optional.FromNillable(value)
|
||||
|
||||
fmt.Println(opt.IsPresent())
|
||||
fmt.Println(opt.IsNotNil())
|
||||
|
||||
|
||||
// Output:
|
||||
@@ -106,13 +106,13 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="Empty">Empty</span>
|
||||
<p>Returns an empty Optional instance.</p>
|
||||
### <span id="Default">Default</span>
|
||||
<p>Returns an default Optional instance.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Empty[T any]() Optional[T]
|
||||
func Default[T any]() Optional[T]
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -125,8 +125,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
optEmpty := OfNullable.Empty[int]()
|
||||
fmt.Println(optEmpty.IsEmpty())
|
||||
optDefault := optional.Default[int]()
|
||||
fmt.Println(optDefault.IsNil())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
@@ -134,13 +134,13 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="IsEmpty">IsEmpty</span>
|
||||
<p>Checks if the Optional is empty.</p>
|
||||
### <span id="IsNil">IsNil</span>
|
||||
<p>Checks if the Optional is nil.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) IsEmpty() bool
|
||||
func (o Optional[T]) IsNil() bool
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -153,8 +153,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
optEmpty := OfNullable.Empty[int]()
|
||||
fmt.Println(optEmpty.IsEmpty())
|
||||
optDefault := optional.Default[int]()
|
||||
fmt.Println(optDefault.IsNil())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
@@ -162,13 +162,13 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="IsPresent">IsPresent</span>
|
||||
<p>Checks if there is a value present.</p>
|
||||
### <span id="IsNotNil">IsNotNil</span>
|
||||
<p>Checks if there is a value not nil.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) IsPresent() bool
|
||||
func (o Optional[T]) IsNotNil() bool
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -182,15 +182,15 @@ import (
|
||||
|
||||
func main() {
|
||||
var value *int = nil
|
||||
opt := optional.OfNullable(value)
|
||||
opt := optional.FromNillable(value)
|
||||
|
||||
fmt.Println(opt.IsPresent())
|
||||
fmt.Println(opt.IsNotNil())
|
||||
|
||||
value = new(int)
|
||||
*value = 42
|
||||
opt = optional.OfNullable(value)
|
||||
opt = optional.FromNillable(value)
|
||||
|
||||
fmt.Println(opt.IsPresent())
|
||||
fmt.Println(opt.IsNotNil())
|
||||
|
||||
|
||||
// Output:
|
||||
@@ -200,13 +200,13 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="IfPresent">IfPresent</span>
|
||||
### <span id="IfNotNil">IfNotNil</span>
|
||||
<p>Performs the given action with the value if a value is present.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) IfPresent(action func(value T))
|
||||
func (o Optional[T]) IfNotNil(action func(value T))
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -222,16 +222,16 @@ func main() {
|
||||
called := false
|
||||
action := func(value int) { called = true }
|
||||
|
||||
optEmpty := optional.Empty[int]()
|
||||
optEmpty.IfPresent(action)
|
||||
optDefault := optional.Default[int]()
|
||||
optDefault.IfNotNil(action)
|
||||
|
||||
fmt.Println(called)
|
||||
|
||||
called = false // Reset for next test
|
||||
optWithValue := optional.Of(42)
|
||||
optWithValue.IfPresent(action)
|
||||
optWithValue.IfNotNil(action)
|
||||
|
||||
fmt.Println(optWithValue.IsPresent())
|
||||
fmt.Println(optWithValue.IsNotNil())
|
||||
|
||||
// Output:
|
||||
// false
|
||||
@@ -240,13 +240,13 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="IfPresentOrElse">IfPresentOrElse</span>
|
||||
<p>Performs the action with the value if present, otherwise performs the empty-based action.</p>
|
||||
### <span id="IfNotNilOrElse">IfNotNilOrElse</span>
|
||||
<p>Performs the action with the value if not nil, otherwise performs the fallback action.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) IfPresentOrElse(action func(value T), emptyAction func())
|
||||
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func())
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -264,7 +264,7 @@ func main() {
|
||||
emptyAction := func() { t.Errorf("Empty action should not be called when value is present") }
|
||||
|
||||
optWithValue := optional.Of(42)
|
||||
optWithValue.IfPresentOrElse(valueAction, emptyAction)
|
||||
optWithValue.IfNotNilOrElse(valueAction, emptyAction)
|
||||
|
||||
fmt.Println(calledWithValue)
|
||||
|
||||
@@ -272,8 +272,8 @@ func main() {
|
||||
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
|
||||
emptyAction = func() { calledWithEmpty = true }
|
||||
|
||||
optEmpty := optional.Empty[int]()
|
||||
optEmpty.IfPresentOrElse(valueAction, emptyAction)
|
||||
optDefault := optional.Default[int]()
|
||||
optDefault.IfNotNilOrElse(valueAction, emptyAction)
|
||||
|
||||
fmt.Println(calledWithEmpty)
|
||||
|
||||
@@ -283,13 +283,13 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Get">Get</span>
|
||||
<p>Returns the value if present, otherwise panics.</p>
|
||||
### <span id="Unwrap">Unwrap</span>
|
||||
<p>Returns the value if not nil, otherwise panics.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) Get() T
|
||||
func (o Optional[T]) Unwrap() T
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -305,7 +305,7 @@ func main() {
|
||||
value := 42
|
||||
opt := optional.Of(value)
|
||||
|
||||
fmt.Println(opt.Get())
|
||||
fmt.Println(opt.Unwrap())
|
||||
|
||||
// Output:
|
||||
// 42
|
||||
@@ -314,7 +314,7 @@ func main() {
|
||||
|
||||
|
||||
### <span id="OrElse">OrElse</span>
|
||||
<p>Returns the value if present, otherwise returns other.</p>
|
||||
<p>Returns the value if not nill, otherwise returns other.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
@@ -332,8 +332,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
optEmpty := optional.Empty[int]()
|
||||
val := optEmpty.OrElse(100)
|
||||
optDefault := optional.Default[int]()
|
||||
val := optDefault.OrElse(100)
|
||||
fmt.Println(val)
|
||||
|
||||
optWithValue := optional.Of(42)
|
||||
@@ -348,12 +348,12 @@ func main() {
|
||||
|
||||
|
||||
### <span id="OrElseGet">OrElseGet</span>
|
||||
<p>Returns the value if present, otherwise invokes supplier and returns the result.</p>
|
||||
<p>Returns the value if not nil, otherwise invokes action and returns the result.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) OrElseGet(supplier func() T) T
|
||||
func (o Optional[T]) OrElseGet(action func() T) T
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -366,10 +366,10 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
optEmpty := optional.Empty[int]()
|
||||
supplier := func() int { return 100 }
|
||||
optDefault := optional.Default[int]()
|
||||
action := func() int { return 100 }
|
||||
|
||||
val := optEmpty.OrElseGet(supplier)
|
||||
val := optDefault.OrElseGet(action)
|
||||
fmt.Println(val)
|
||||
|
||||
// Output:
|
||||
@@ -378,13 +378,13 @@ func main() {
|
||||
```
|
||||
|
||||
|
||||
### <span id="OrElseThrow">OrElseThrow</span>
|
||||
### <span id="OrElseTrigger">OrElseTrigger</span>
|
||||
<p>Returns the value if present, otherwise returns an error.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (o Optional[T]) OrElseThrow(errorSupplier func() error) (T, error)
|
||||
OrElseTrigger(errorHandler func() error) (T, error)
|
||||
```
|
||||
<b>Example:</b>
|
||||
|
||||
@@ -397,13 +397,13 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
optEmpty := optional.Empty[int]()
|
||||
_, err := optEmpty.OrElseThrow(func() error { return errors.New("no value") })
|
||||
optDefault := optional.Default[int]()
|
||||
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
|
||||
|
||||
fmt.Println(err.Error())
|
||||
|
||||
optWithValue := optional.Of(42)
|
||||
val, err := optWithValue.OrElseThrow(func() error { return errors.New("no value") })
|
||||
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
|
||||
|
||||
fmt.Println(val)
|
||||
fmt.Println(err)
|
||||
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
|
||||
## Index
|
||||
|
||||
- [NewSet](#NewSet)
|
||||
- [NewSetFromSlice](#NewSetFromSlice)
|
||||
- [New](#New)
|
||||
- [FromSlice](#FromSlice)
|
||||
- [Values](#Values)
|
||||
- [Add](#Add)
|
||||
- [AddIfNotExist](#AddIfNotExist)
|
||||
@@ -41,20 +41,23 @@ import (
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
- [Pop](#Pop)
|
||||
- [ToSlice](#ToSlice)
|
||||
- [ToSortedSlice](#ToSortedSlice)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
## Documentation
|
||||
|
||||
### <span id="NewSet">NewSet</span>
|
||||
### <span id="New">New</span>
|
||||
|
||||
<p>Create a set instance</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type Set[T comparable] map[T]bool
|
||||
func NewSet[T comparable](items ...T) Set[T]
|
||||
type Set[T comparable] map[T]struct{}
|
||||
func New[T comparable](items ...T) Set[T]
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
@@ -68,19 +71,19 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int](1,2,2,3)
|
||||
st := set.New[int](1,2,2,3)
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="NewSetFromSlice">NewSetFromSlice</span>
|
||||
### <span id="FromSlice">FromSlice</span>
|
||||
|
||||
<p>Create a set from slice</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func NewSetFromSlice[T comparable](items []T) Set[T]
|
||||
func FromSlice[T comparable](items []T) Set[T]
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
@@ -94,14 +97,15 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
|
||||
st := set.FromSlice([]int{1, 2, 2, 3})
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Values">Values</span>
|
||||
### <span id="Values">Values<sup>deprecated</sup></span>
|
||||
|
||||
<p>Return slice of all set data</p>
|
||||
<p>Return slice of all set data.<br>
|
||||
The <a href='#ToSlice'>ToSlice()</a> function provides the same functionality as Values and returns a slice containing all values of the set.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
@@ -120,7 +124,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int](1,2,2,3)
|
||||
st := set.New[int](1,2,2,3)
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
}
|
||||
```
|
||||
@@ -146,7 +150,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st := set.New[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
fmt.Println(st.Values()) //1,2,3
|
||||
@@ -174,7 +178,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st := set.New[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
r1 := st.AddIfNotExist(1)
|
||||
@@ -207,7 +211,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st := set.New[int]()
|
||||
st.Add(1, 2)
|
||||
|
||||
ok := st.AddIfNotExistBy(3, func(val int) bool {
|
||||
@@ -246,7 +250,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st := set.New[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
set.Delete(3)
|
||||
@@ -275,7 +279,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
st := set.NewSet[int]()
|
||||
st := set.New[int]()
|
||||
st.Add(1, 2, 3)
|
||||
|
||||
fmt.Println(st.Contain(1)) //true
|
||||
@@ -304,9 +308,9 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(1, 2)
|
||||
set3 := set.NewSet(1, 2, 3, 4)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(1, 2)
|
||||
set3 := set.New(1, 2, 3, 4)
|
||||
|
||||
fmt.Println(set1.ContainAll(set2)) //true
|
||||
fmt.Println(set1.ContainAll(set3)) //false
|
||||
@@ -334,7 +338,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set1 := set.New(1, 2, 3)
|
||||
|
||||
fmt.Println(set1.Size()) //3
|
||||
}
|
||||
@@ -361,7 +365,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set1.Clone()
|
||||
|
||||
fmt.Println(set1.Size() == set2.Size()) //true
|
||||
@@ -390,9 +394,9 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(1, 2, 3)
|
||||
set3 := set.NewSet(1, 2, 3, 4)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(1, 2, 3)
|
||||
set3 := set.New(1, 2, 3, 4)
|
||||
|
||||
fmt.Println(set1.Equal(set2)) //true
|
||||
fmt.Println(set1.Equal(set3)) //false
|
||||
@@ -420,7 +424,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set1 := set.New(1, 2, 3)
|
||||
arr := []int{}
|
||||
set.Iterate(func(item int) {
|
||||
arr = append(arr, item)
|
||||
@@ -451,7 +455,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := set.NewSet(1, 2, 3, 4, 5)
|
||||
s := set.New(1, 2, 3, 4, 5)
|
||||
|
||||
var sum int
|
||||
|
||||
@@ -488,8 +492,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet()
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New()
|
||||
|
||||
fmt.Println(set1.IsEmpty()) //false
|
||||
fmt.Println(set2.IsEmpty()) //true
|
||||
@@ -517,8 +521,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(2, 3, 4, 5)
|
||||
set3 := set1.Union(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //1,2,3,4,5
|
||||
@@ -546,8 +550,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(2, 3, 4, 5)
|
||||
set3 := set1.Intersection(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //2,3
|
||||
@@ -575,8 +579,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(2, 3, 4, 5)
|
||||
set3 := set1.SymmetricDifference(set2)
|
||||
|
||||
fmt.Println(set3.Values()) //1,4,5
|
||||
@@ -604,9 +608,9 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
set1 := set.NewSet(1, 2, 3)
|
||||
set2 := set.NewSet(2, 3, 4, 5)
|
||||
set3 := set.NewSet(2, 3)
|
||||
set1 := set.New(1, 2, 3)
|
||||
set2 := set.New(2, 3, 4, 5)
|
||||
set3 := set.New(2, 3)
|
||||
|
||||
res1 := set1.Minus(set2)
|
||||
fmt.Println(res1.Values()) //1
|
||||
@@ -637,7 +641,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := set.NewSet[int]()
|
||||
s := set.New[int]()
|
||||
s.Add(1)
|
||||
s.Add(2)
|
||||
s.Add(3)
|
||||
@@ -648,3 +652,58 @@ func main() {
|
||||
fmt.Println(ok) // true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToSlice">ToSlice</span>
|
||||
|
||||
<p>returns a slice containing all values of the set.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) ToSlice() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
s := set.New(1, 2, 3, 4, 5)
|
||||
|
||||
val := s.ToSlice()
|
||||
fmt.Println(val) // [2 3 4 5 1]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ToSortedSlice">ToSortedSlice</span>
|
||||
|
||||
<p>returns a sorted slice containing all values of the set</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func (s Set[T]) ToSortedSlice() (v T, ok bool)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
s1 := set.New(1, 2, 3, 4, 5)
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
|
||||
|
||||
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
|
||||
return v1 < v2
|
||||
})
|
||||
|
||||
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
|
||||
return v1.Age < v2.Age
|
||||
})
|
||||
|
||||
fmt.Println(res1) // [1 2 3 4 5]
|
||||
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
- [CreateFile](#CreateFile)
|
||||
- [CreateDir](#CreateDir)
|
||||
- [CopyFile](#CopyFile)
|
||||
- [CopyDir](#CopyDir)
|
||||
- [CurrentPath](#CurrentPath)
|
||||
- [FileMode](#FileMode)
|
||||
- [MiMeType](#MiMeType)
|
||||
@@ -49,6 +50,8 @@ import (
|
||||
- [WriteStringToFile](#WriteStringToFile)
|
||||
- [WriteBytesToFile](#WriteBytesToFile)
|
||||
- [ReadFile](#ReadFile)
|
||||
- [ChunkRead](#ChunkRead)
|
||||
- [ParallelChunkRead](#ParallelChunkRead)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -162,6 +165,34 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CopyDir">CopyDir</span>
|
||||
|
||||
<p>copy src directory to dst directory, it will copy all files and directories recursively. the access permission will be the same as the source directory. if dstPath exists, it will return an error.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CopyDir(srcPath string, dstPath string) error
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/YAyFTA_UuPb)</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := fileutil.CopyFile("./test_src", "./test_dest")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CurrentPath">CurrentPath</span>
|
||||
|
||||
<p>return current absolute path.</p>
|
||||
@@ -932,3 +963,115 @@ func main() {
|
||||
// Disallow: /deny
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ChunkRead">ChunkRead</span>
|
||||
|
||||
<p>reads a block from the file at the specified offset and returns all lines within the block.</p>
|
||||
|
||||
<b>Signature :</b>
|
||||
|
||||
```go
|
||||
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const mb = 1024 * 1024
|
||||
const defaultChunkSizeMB = 100
|
||||
|
||||
// test1.csv file content:
|
||||
// Lili,22,female
|
||||
// Jim,21,male
|
||||
filePath := "./testdata/test1.csv"
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 0, defaultChunkSizeMB*mb)
|
||||
},
|
||||
}
|
||||
|
||||
lines, err := fileutil.ChunkRead(f, 0, 100, &bufPool)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(lines[0])
|
||||
fmt.Println(lines[1])
|
||||
|
||||
// Output:
|
||||
// Lili,22,female
|
||||
// Jim,21,male
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="ParallelChunkRead">ParallelChunkRead</span>
|
||||
|
||||
<p>Reads the file in parallel and send each chunk of lines to the specified channel.</p>
|
||||
|
||||
<b>Signature :</b>
|
||||
|
||||
```go
|
||||
// filePath: file path.
|
||||
// chunkSizeMB: The size of the block (in MB, the default is 100MB when set to 0). Setting it too large will be detrimental. Adjust it as appropriate.
|
||||
// maxGoroutine: The number of concurrent read chunks, the number of CPU cores used when set to 0.
|
||||
// linesCh: The channel used to receive the returned results.
|
||||
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/fileutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const mb = 1024 * 1024
|
||||
const defaultChunkSizeMB = 100 // 默认值
|
||||
|
||||
numParsers := runtime.NumCPU()
|
||||
|
||||
linesCh := make(chan []string, numParsers)
|
||||
|
||||
// test1.csv file content:
|
||||
// Lili,22,female
|
||||
// Jim,21,male
|
||||
filePath := "./testdata/test1.csv"
|
||||
|
||||
go fileutil.ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
|
||||
|
||||
var totalLines int
|
||||
for lines := range linesCh {
|
||||
totalLines += len(lines)
|
||||
|
||||
for _, line := range lines {
|
||||
fmt.Println(line)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(totalLines)
|
||||
|
||||
// Output:
|
||||
// Lili,22,female
|
||||
// Jim,21,male
|
||||
// 2
|
||||
}
|
||||
```
|
||||
@@ -37,6 +37,9 @@ import (
|
||||
- [Or](#Or)
|
||||
- [Negate](#Negate)
|
||||
- [Nor](#Nor)
|
||||
- [Xnor](#Xnor)
|
||||
- [Nand](#Nand)
|
||||
- [AcceptIf](#AcceptIf)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -562,4 +565,131 @@ func main() {
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Nand">Nand</span>
|
||||
|
||||
<p>Returns a composed predicate that represents the logical NAND of a list of predicates. It evaluates to true only if all predicates evaluate to false for the given value.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Nand[T any](predicates ...func(T) bool) func(T) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isNumericAndLength5 := function.Nand(
|
||||
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||
func(s string) bool { return len(s) == 5 },
|
||||
)
|
||||
|
||||
fmt.Println(isNumericAndLength5("12345"))
|
||||
fmt.Println(isNumericAndLength5("1234"))
|
||||
fmt.Println(isNumericAndLength5("abcdef"))
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Xnor">Xnor</span>
|
||||
|
||||
<p>Returns a composed predicate that represents the logical XNOR of a list of predicates. It evaluates to true only if all predicates evaluate to true or false for the given value.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Xnor[T any](predicates ...func(T) bool) func(T) bool
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
isEven := func(i int) bool { return i%2 == 0 }
|
||||
isPositive := func(i int) bool { return i > 0 }
|
||||
|
||||
match := function.Xnor(isEven, isPositive)
|
||||
|
||||
fmt.Println(match(2))
|
||||
fmt.Println(match(-3))
|
||||
fmt.Println(match(3))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="AcceptIf">AcceptIf</span>
|
||||
|
||||
<p>AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
|
||||
A predicate function that takes an argument of type T and returns a bool.
|
||||
An apply function that also takes an argument of type T and returns a modified value of the same type.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
adder := AcceptIf(
|
||||
And(
|
||||
func(x int) bool {
|
||||
return x > 10
|
||||
}, func(x int) bool {
|
||||
return x%2 == 0
|
||||
}),
|
||||
func(x int) int {
|
||||
return x + 1
|
||||
},
|
||||
)
|
||||
|
||||
result, ok := adder(20)
|
||||
fmt.Println(result)
|
||||
fmt.Println(ok)
|
||||
|
||||
result, ok = adder(21)
|
||||
fmt.Println(result)
|
||||
fmt.Println(ok)
|
||||
|
||||
// Output:
|
||||
// 21
|
||||
// true
|
||||
// 0
|
||||
// false
|
||||
}
|
||||
|
||||
```
|
||||
@@ -34,6 +34,10 @@ import (
|
||||
- [RoundToFloat](#RoundToFloat)
|
||||
- [RoundToString](#RoundToString)
|
||||
- [TruncRound](#TruncRound)
|
||||
- [CeilToFloat](#CeilToFloat)
|
||||
- [CeilToString](#CeilToString)
|
||||
- [FloorToFloat](#FloorToFloat)
|
||||
- [FloorToString](#FloorToString)
|
||||
- [Range](#Range)
|
||||
- [RangeWithStep](#RangeWithStep)
|
||||
- [AngleToRadian](#AngleToRadian)
|
||||
@@ -47,6 +51,7 @@ import (
|
||||
- [Log](#Log)
|
||||
- [Sum](#Sum)
|
||||
- [Abs](#Abs)
|
||||
- [Div](#Div)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -392,7 +397,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RoundToFloat(x float64, n int) float64
|
||||
func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ghyb528JRJL)</span></b>
|
||||
@@ -428,7 +433,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RoundToString(x float64, n int) string
|
||||
func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/kZwpBRAcllO)</span></b>
|
||||
@@ -464,7 +469,7 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func TruncRound(x float64, n int) float64
|
||||
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/aumarSHIGzP)</span></b>
|
||||
@@ -493,6 +498,150 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CeilToFloat">CeilToFloat</span>
|
||||
|
||||
<p>Round float up n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.CeilToFloat(3.14159, 1)
|
||||
result2 := mathutil.CeilToFloat(3.14159, 2)
|
||||
result3 := mathutil.CeilToFloat(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.2
|
||||
// 3.15
|
||||
// 5
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="CeilToString">CeilToString</span>
|
||||
|
||||
<p>Round float up n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.CeilToString(3.14159, 1)
|
||||
result2 := mathutil.CeilToString(3.14159, 2)
|
||||
result3 := mathutil.CeilToString(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.2
|
||||
// 3.15
|
||||
// 5.0000
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FloorToFloat">FloorToFloat</span>
|
||||
|
||||
<p>Round float down n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.FloorToFloat(3.14159, 1)
|
||||
result2 := mathutil.FloorToFloat(3.14159, 2)
|
||||
result3 := mathutil.FloorToFloat(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.1
|
||||
// 3.14
|
||||
// 5
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="FloorToString">FloorToString</span>
|
||||
|
||||
<p>Round float down n decimal places.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.FloorToString(3.14159, 1)
|
||||
result2 := mathutil.FloorToString(3.14159, 2)
|
||||
result3 := mathutil.FloorToString(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.1
|
||||
// 3.14
|
||||
// 5.0000
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Range">Range</span>
|
||||
|
||||
<p>Creates a slice of numbers from start with specified count, element step is 1.</p>
|
||||
@@ -964,17 +1113,52 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := Abs(-1)
|
||||
result2 := Abs(-0.1)
|
||||
result3 := Abs(float32(0.2))
|
||||
result1 := mathutil.Abs(-1)
|
||||
result2 := mathutil.Abs(-0.1)
|
||||
result3 := mathutil.Abs(float32(0.2))
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 0.1
|
||||
// 0.2
|
||||
// Output:
|
||||
// 1
|
||||
// 0.1
|
||||
// 0.2
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Div">Div</span>
|
||||
|
||||
<p>returns the result of x divided by y.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Div[T constraints.Float | constraints.Integer](x T, y T) float64
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := mathutil.Div(9, 4)
|
||||
result2 := mathutil.Div(1, 2)
|
||||
result3 := mathutil.Div(0, 666)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
// Output:
|
||||
// 2.25
|
||||
// 0.5
|
||||
// 0
|
||||
}
|
||||
```
|
||||
@@ -27,6 +27,10 @@ import (
|
||||
- [RetryFunc](#RetryFunc)
|
||||
- [RetryDuration](#RetryDuration)
|
||||
- [RetryTimes](#RetryTimes)
|
||||
- [BackoffStrategy](#BackoffStrategy)
|
||||
- [RetryWithCustomBackoff](#RetryWithCustomBackoff)
|
||||
- [RetryWithLinearBackoff](#RetryWithLinearBackoff)
|
||||
- [RetryWithExponentialWithJitterBackoff](#RetryWithExponentialWithJitterBackoff)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -259,3 +263,202 @@ func main() {
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="BackoffStrategy">BackoffStrategy</span>
|
||||
|
||||
<p>An interface that defines a method for calculating backoff intervals.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
// BackoffStrategy is an interface that defines a method for calculating backoff intervals.
|
||||
type BackoffStrategy interface {
|
||||
// CalculateInterval returns the time.Duration after which the next retry attempt should be made.
|
||||
CalculateInterval() time.Duration
|
||||
}
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/v2/retry"
|
||||
)
|
||||
|
||||
type ExampleCustomBackoffStrategy struct {
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
|
||||
return c.interval + 1
|
||||
}
|
||||
|
||||
func main() {
|
||||
number := 0
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == 3 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(number)
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RetryWithCustomBackoff">RetryWithCustomBackoff</span>
|
||||
|
||||
<p>Set abitary custom backoff strategy.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/v2/retry"
|
||||
)
|
||||
|
||||
type ExampleCustomBackoffStrategy struct {
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
|
||||
return c.interval + 1
|
||||
}
|
||||
|
||||
func main() {
|
||||
number := 0
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == 3 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := retry,Retry(increaseNumber, retry.RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(number)
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="RetryWithLinearBackoff">RetryWithLinearBackoff</span>
|
||||
|
||||
<p>Set linear strategy backoff.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RetryWithLinearBackoff(interval time.Duration) Option
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/v2/retry"
|
||||
)
|
||||
|
||||
func main() {
|
||||
number := 0
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == 3 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := retry.Retry(increaseNumber, retry.RetryWithLinearBackoff(time.Microsecond*50))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(number)
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### <span id="RetryWithExponentialWithJitterBackoff">RetryWithExponentialWithJitterBackoff</span>
|
||||
|
||||
<p>Set exponential strategy backoff.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"log"
|
||||
"github.com/duke-git/lancet/v2/retry"
|
||||
)
|
||||
|
||||
func main() {
|
||||
number := 0
|
||||
increaseNumber := func() error {
|
||||
number++
|
||||
if number == 3 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("error occurs")
|
||||
}
|
||||
|
||||
err := retry.Retry(increaseNumber, retry.RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(number)
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2565,4 +2565,35 @@ func main() {
|
||||
// Output:
|
||||
// okk
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="SetToDefaultIf">SetToDefaultIf</span>
|
||||
|
||||
<p>Sets elements to their default value if they match the given predicate. It retains the positions of the elements in the slice. It returns slice of T and the count of modified slice items</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||
modifiedStrs, count := slice.SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||
|
||||
fmt.Println(modifiedStrs)
|
||||
fmt.Println(count)
|
||||
|
||||
// Output:
|
||||
// [ b c d ]
|
||||
// 3
|
||||
}
|
||||
```
|
||||
@@ -60,6 +60,8 @@ import (
|
||||
- [ContainsAll](#ContainsAll)
|
||||
- [ContainsAny](#ContainsAny)
|
||||
- [RemoveWhiteSpace](#RemoveWhiteSpace)
|
||||
- [SubInBetween](#SubInBetween)
|
||||
- [HammingDistance](#HammingDistance)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -1496,4 +1498,36 @@ func main() {
|
||||
// abc
|
||||
// bc
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="HammingDistance">HammingDistance</span>
|
||||
|
||||
<p>HammingDistance calculates the Hamming distance between two strings. The Hamming distance is the number of positions at which the corresponding symbols are different.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
HammingDistance(a, b string) (int, error)
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
result1, _ := strutil.HammingDistance("de", "de")
|
||||
result2, _ := strutil.HammingDistance("a", "d")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
|
||||
// Output:
|
||||
// 0
|
||||
// 1
|
||||
}
|
||||
```
|
||||
@@ -30,7 +30,7 @@ We are excited that you are interested in contributing to lancet. Before submitt
|
||||
|
||||
- Before submitting a PR, please execute the unit test command: `go test -v ./...` to ensure that all unit test tasks should pass.
|
||||
|
||||
- Make sure PRs are created to `v2` branch instead of `master` branch.
|
||||
- Make sure PRs are created to `rc` branch instead of other branch.
|
||||
|
||||
- If your PR fixes a bug, please provide a description about the related bug.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代
|
||||
|
||||
- 提交 PR 前请执行单元测试命令:go test -v ./...,确保所有单元测试任务通过。
|
||||
|
||||
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
|
||||
- 确保 PR 是提交到 `rc` 分支,而不是其他分支。
|
||||
|
||||
- 如果是修复 bug,请在 PR 中给出描述信息。
|
||||
|
||||
|
||||
157
fileutil/file.go
157
fileutil/file.go
@@ -22,6 +22,7 @@ import (
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
@@ -113,6 +114,65 @@ func CreateDir(absPath string) error {
|
||||
return os.MkdirAll(absPath, os.ModePerm)
|
||||
}
|
||||
|
||||
// CopyDir copy src directory to dst directory, it will copy all files and directories recursively.
|
||||
// the access permission will be the same as the source directory.
|
||||
// if dstPath exists, it will return an error.
|
||||
// Play: https://go.dev/play/p/YAyFTA_UuPb
|
||||
func CopyDir(srcPath string, dstPath string) error {
|
||||
if !IsDir(srcPath) {
|
||||
return errors.New("source path is not a directory")
|
||||
}
|
||||
var err error
|
||||
srcPath, err = filepath.Abs(srcPath)
|
||||
if err != nil {
|
||||
return 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
|
||||
srcFileInfo, err := os.Stat(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create dstPath with srcPath's mode
|
||||
err = os.MkdirAll(dstPath, srcFileInfo.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error {
|
||||
if srcPath == path {
|
||||
return nil
|
||||
}
|
||||
curDstPath := filepath.Join(dstPath, filepath.Base(path))
|
||||
if info.IsDir() {
|
||||
err = CopyDir(path, curDstPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = CopyFile(path, curDstPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chmod(curDstPath, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// IsDir checks if the path is directory or not.
|
||||
// Play: https://go.dev/play/p/WkVwEKqtOWk
|
||||
func IsDir(path string) bool {
|
||||
@@ -377,7 +437,7 @@ func UnZip(zipFile string, destPath string) error {
|
||||
defer zipReader.Close()
|
||||
|
||||
for _, f := range zipReader.File {
|
||||
//issue#62: fix ZipSlip bug
|
||||
// issue#62: fix ZipSlip bug
|
||||
path, err := safeFilepathJoin(destPath, f.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -807,3 +867,98 @@ func isCsvSupportedType(v interface{}) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ChunkRead reads a block from the file at the specified offset and returns all lines within the block
|
||||
// Play: todo
|
||||
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error) {
|
||||
buf := bufPool.Get().([]byte)[:size] // 从Pool获取缓冲区并调整大小
|
||||
n, err := file.ReadAt(buf, offset) // 从指定偏移读取数据到缓冲区
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
buf = buf[:n] // 调整切片以匹配实际读取的字节数
|
||||
|
||||
var lines []string
|
||||
var lineStart int
|
||||
for i, b := range buf {
|
||||
if b == '\n' {
|
||||
line := string(buf[lineStart:i]) // 不包括换行符
|
||||
lines = append(lines, line)
|
||||
lineStart = i + 1 // 设置下一行的开始
|
||||
}
|
||||
}
|
||||
|
||||
if lineStart < len(buf) { // 处理块末尾的行
|
||||
line := string(buf[lineStart:])
|
||||
lines = append(lines, line)
|
||||
}
|
||||
bufPool.Put(buf) // 读取完成后,将缓冲区放回Pool
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
// ParallelChunkRead reads the file in parallel and send each chunk of lines to the specified channel.
|
||||
// filePath 文件路径
|
||||
// chunkSizeMB 分块的大小(单位MB,设置为0时使用默认100MB),设置过大反而不利,视情调整
|
||||
// maxGoroutine 并发读取分块的数量,设置为0时使用CPU核心数
|
||||
// linesCh用于接收返回结果的通道。
|
||||
// Play: todo
|
||||
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error {
|
||||
if chunkSizeMB == 0 {
|
||||
chunkSizeMB = 100
|
||||
}
|
||||
chunkSize := chunkSizeMB * 1024 * 1024
|
||||
// 内存复用
|
||||
bufPool := sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 0, chunkSize)
|
||||
},
|
||||
}
|
||||
|
||||
if maxGoroutine == 0 {
|
||||
maxGoroutine = runtime.NumCPU() // 设置为0时使用CPU核心数
|
||||
}
|
||||
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
info, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
chunkOffsetCh := make(chan int64, maxGoroutine)
|
||||
|
||||
// 分配工作
|
||||
go func() {
|
||||
for i := int64(0); i < info.Size(); i += int64(chunkSize) {
|
||||
chunkOffsetCh <- i
|
||||
}
|
||||
close(chunkOffsetCh)
|
||||
}()
|
||||
|
||||
// 启动工作协程
|
||||
for i := 0; i < maxGoroutine; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for chunkOffset := range chunkOffsetCh {
|
||||
chunk, err := ChunkRead(f, chunkOffset, chunkSize, &bufPool)
|
||||
if err == nil {
|
||||
linesCh <- chunk
|
||||
}
|
||||
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// 等待所有解析完成后关闭行通道
|
||||
wg.Wait()
|
||||
close(linesCh)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func ExampleIsExist() {
|
||||
@@ -421,8 +423,69 @@ func ExampleReadFile() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(dat))
|
||||
|
||||
// Output:
|
||||
// User-agent: *
|
||||
// Disallow: /deny
|
||||
}
|
||||
|
||||
func ExampleChunkRead() {
|
||||
const mb = 1024 * 1024
|
||||
const defaultChunkSizeMB = 100
|
||||
|
||||
filePath := "./testdata/test1.csv"
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 0, defaultChunkSizeMB*mb)
|
||||
},
|
||||
}
|
||||
|
||||
lines, err := ChunkRead(f, 0, 100, &bufPool)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(lines[0])
|
||||
fmt.Println(lines[1])
|
||||
|
||||
// Output:
|
||||
// Lili,22,female
|
||||
// Jim,21,male
|
||||
}
|
||||
|
||||
func ExampleParallelChunkRead() {
|
||||
const mb = 1024 * 1024
|
||||
const defaultChunkSizeMB = 100 // 默认值
|
||||
|
||||
numParsers := runtime.NumCPU()
|
||||
|
||||
linesCh := make(chan []string, numParsers)
|
||||
filePath := "./testdata/test1.csv"
|
||||
|
||||
go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
|
||||
|
||||
var totalLines int
|
||||
for lines := range linesCh {
|
||||
totalLines += len(lines)
|
||||
|
||||
for _, line := range lines {
|
||||
fmt.Println(line)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(totalLines)
|
||||
|
||||
// Output:
|
||||
// Lili,22,female
|
||||
// Jim,21,male
|
||||
// 2
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ package fileutil
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
@@ -544,3 +548,74 @@ func TestReadlineFile(t *testing.T) {
|
||||
internal.NewAssert(t, "TestReadlineFile").Equal(line, lineRead)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyDir(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestCopyDir")
|
||||
|
||||
src := "./testdata"
|
||||
dest := "./testdata_copy"
|
||||
|
||||
err := CopyDir(src, dest)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(true, IsExist(dest))
|
||||
|
||||
filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
|
||||
destPath := strings.Replace(path, src, dest, 1)
|
||||
assert.Equal(true, IsExist(destPath))
|
||||
return nil
|
||||
})
|
||||
|
||||
os.RemoveAll(dest)
|
||||
}
|
||||
|
||||
func TestParallelChunkRead(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestParallelChunkRead")
|
||||
|
||||
const mb = 1024 * 1024
|
||||
const defaultChunkSizeMB = 100 // 默认值
|
||||
|
||||
numParsers := runtime.NumCPU()
|
||||
|
||||
linesCh := make(chan []string, numParsers)
|
||||
filePath := "./testdata/test1.csv" // 替换为你的文件路径
|
||||
|
||||
go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
|
||||
|
||||
var totalLines int
|
||||
for lines := range linesCh {
|
||||
totalLines += len(lines)
|
||||
|
||||
assert.Equal("Lili,22,female", lines[0])
|
||||
assert.Equal("Jim,21,male", lines[1])
|
||||
}
|
||||
|
||||
assert.Equal(2, totalLines)
|
||||
}
|
||||
|
||||
func TestChunkRead(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestChunkRead")
|
||||
|
||||
const mb = 1024 * 1024
|
||||
const defaultChunkSizeMB = 100 // 默认值
|
||||
|
||||
filePath := "./testdata/test1.csv" // 替换为你的文件路径
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 0, defaultChunkSizeMB*mb)
|
||||
},
|
||||
}
|
||||
|
||||
lines, err := ChunkRead(f, 0, 100, &bufPool)
|
||||
|
||||
assert.Equal("Lili,22,female", lines[0])
|
||||
assert.Equal("Jim,21,male", lines[1])
|
||||
|
||||
}
|
||||
|
||||
1
fileutil/testdata/test01/demo2.csv
vendored
Normal file
1
fileutil/testdata/test01/demo2.csv
vendored
Normal file
@@ -0,0 +1 @@
|
||||
makj1
|
||||
|
3
fileutil/testdata/test1.csv
vendored
3
fileutil/testdata/test1.csv
vendored
@@ -1,5 +1,2 @@
|
||||
Lili,22,female
|
||||
Jim,21,male
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -136,6 +136,27 @@ func Pipeline[T any](funcs ...func(T) T) func(T) T {
|
||||
}
|
||||
}
|
||||
|
||||
// AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
|
||||
// A predicate function that takes an argument of type T and returns a bool.
|
||||
// An apply function that also takes an argument of type T and returns a modified value of the same type.
|
||||
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool) {
|
||||
if predicate == nil {
|
||||
panic("programming error: predicate must be not nil")
|
||||
}
|
||||
|
||||
if apply == nil {
|
||||
panic("programming error: apply must be not nil")
|
||||
}
|
||||
|
||||
return func(t T) (T, bool) {
|
||||
if !predicate(t) {
|
||||
var defaultValue T
|
||||
return defaultValue, false
|
||||
}
|
||||
return apply(t), true
|
||||
}
|
||||
}
|
||||
|
||||
func unsafeInvokeFunc(fn any, args ...any) []reflect.Value {
|
||||
fv := reflect.ValueOf(fn)
|
||||
params := make([]reflect.Value, len(args))
|
||||
|
||||
@@ -145,3 +145,32 @@ func ExamplePipeline() {
|
||||
// Output:
|
||||
// 36
|
||||
}
|
||||
|
||||
func ExampleAcceptIf() {
|
||||
|
||||
adder := AcceptIf(
|
||||
And(
|
||||
func(x int) bool {
|
||||
return x > 10
|
||||
}, func(x int) bool {
|
||||
return x%2 == 0
|
||||
}),
|
||||
func(x int) int {
|
||||
return x + 1
|
||||
},
|
||||
)
|
||||
|
||||
result, ok := adder(20)
|
||||
fmt.Println(result)
|
||||
fmt.Println(ok)
|
||||
|
||||
result, ok = adder(21)
|
||||
fmt.Println(result)
|
||||
fmt.Println(ok)
|
||||
|
||||
// Output:
|
||||
// 21
|
||||
// true
|
||||
// 0
|
||||
// false
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestBefore(t *testing.T) {
|
||||
|
||||
var res []int64
|
||||
type cb func(args ...any) []reflect.Value
|
||||
appendStr := func(i int, s string, fn cb) {
|
||||
appendStr := func(i int, _ string, fn cb) {
|
||||
v := fn(i)
|
||||
res = append(res, v[0].Int())
|
||||
}
|
||||
@@ -162,3 +162,63 @@ func TestPipeline(t *testing.T) {
|
||||
|
||||
assert.Equal(36, f(2))
|
||||
}
|
||||
|
||||
func TestAcceptIf(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "AcceptIf")
|
||||
|
||||
adder := AcceptIf(
|
||||
And(
|
||||
func(x int) bool {
|
||||
return x > 10
|
||||
}, func(x int) bool {
|
||||
return x%2 == 0
|
||||
}),
|
||||
func(x int) int {
|
||||
return x + 1
|
||||
},
|
||||
)
|
||||
|
||||
result, ok := adder(20)
|
||||
assert.Equal(21, result)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
result, ok = adder(21)
|
||||
assert.Equal(0, result)
|
||||
assert.Equal(false, ok)
|
||||
}
|
||||
|
||||
func TestAcceptIfPanicMissingPredicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestAcceptIfPanicMissingPredicate")
|
||||
|
||||
defer func() {
|
||||
v := recover()
|
||||
assert.Equal("programming error: predicate must be not nil", v)
|
||||
}()
|
||||
|
||||
AcceptIf(
|
||||
nil,
|
||||
func(x int) int {
|
||||
return x
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestAcceptIfPanicMissingApply(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestAcceptIfPanicMissingApply")
|
||||
|
||||
defer func() {
|
||||
v := recover()
|
||||
assert.Equal("programming error: apply must be not nil", v)
|
||||
}()
|
||||
|
||||
AcceptIf(
|
||||
func(i int) bool {
|
||||
return false
|
||||
},
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ package function
|
||||
// And returns a composed predicate that represents the logical AND of a list of predicates.
|
||||
// It evaluates to true only if all predicates evaluate to true for the given value.
|
||||
func And[T any](predicates ...func(T) bool) func(T) bool {
|
||||
if len(predicates) < 2 {
|
||||
panic("programming error: predicates count must be at least 2")
|
||||
}
|
||||
return func(value T) bool {
|
||||
for _, predicate := range predicates {
|
||||
if !predicate(value) {
|
||||
@@ -13,6 +16,22 @@ 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.
|
||||
// It evaluates to true only if all predicates evaluate to false for the given value.
|
||||
func Nand[T any](predicates ...func(T) bool) func(T) bool {
|
||||
if len(predicates) < 2 {
|
||||
panic("programming error: predicates count must be at least 2")
|
||||
}
|
||||
return func(value T) bool {
|
||||
for _, predicate := range predicates {
|
||||
if predicate(value) {
|
||||
return false // Short-circuit on the first true predicate
|
||||
}
|
||||
}
|
||||
return true // True if all predicates are false
|
||||
}
|
||||
}
|
||||
|
||||
// Negate returns a predicate that represents the logical negation of this predicate.
|
||||
func Negate[T any](predicate func(T) bool) func(T) bool {
|
||||
return func(value T) bool {
|
||||
@@ -23,6 +42,9 @@ 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.
|
||||
// It evaluates to true if at least one of the predicates evaluates to true for the given value.
|
||||
func Or[T any](predicates ...func(T) bool) func(T) bool {
|
||||
if len(predicates) < 2 {
|
||||
panic("programming error: predicates count must be at least 2")
|
||||
}
|
||||
return func(value T) bool {
|
||||
for _, predicate := range predicates {
|
||||
if predicate(value) {
|
||||
@@ -36,6 +58,9 @@ 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.
|
||||
// It evaluates to true only if all predicates evaluate to false for the given value.
|
||||
func Nor[T any](predicates ...func(T) bool) func(T) bool {
|
||||
if len(predicates) < 2 {
|
||||
panic("programming error: predicates count must be at least 2")
|
||||
}
|
||||
return func(value T) bool {
|
||||
for _, predicate := range predicates {
|
||||
if predicate(value) {
|
||||
@@ -45,3 +70,22 @@ func Nor[T any](predicates ...func(T) bool) func(T) bool {
|
||||
return true // Only returns true if all predicates evaluate to false
|
||||
}
|
||||
}
|
||||
|
||||
// Xnor returns a composed predicate that represents the logical XNOR of a list of predicates.
|
||||
// It evaluates to true only if all predicates evaluate to true or false for the given value.
|
||||
func Xnor[T any](predicates ...func(T) bool) func(T) bool {
|
||||
if len(predicates) < 2 {
|
||||
panic("programming error: predicates count must be at least 2")
|
||||
}
|
||||
return func(value T) bool {
|
||||
trueCount := 0
|
||||
for _, predicate := range predicates {
|
||||
if predicate(value) {
|
||||
trueCount++
|
||||
}
|
||||
}
|
||||
// XNOR is true if either all predicates are true or all are false
|
||||
// This is the same as saying trueCount is 0 (all false) or trueCount is len(predicates) (all true)
|
||||
return trueCount == 0 || trueCount == len(predicates)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,22 @@ func ExampleAnd() {
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleNand() {
|
||||
isNumericAndLength5 := Nand(
|
||||
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||
func(s string) bool { return len(s) == 5 },
|
||||
)
|
||||
|
||||
fmt.Println(isNumericAndLength5("12345"))
|
||||
fmt.Println(isNumericAndLength5("1234"))
|
||||
fmt.Println(isNumericAndLength5("abcdef"))
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleNor() {
|
||||
match := Nor(
|
||||
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||
@@ -73,6 +89,22 @@ func ExampleNor() {
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleXnor() {
|
||||
isEven := func(i int) bool { return i%2 == 0 }
|
||||
isPositive := func(i int) bool { return i > 0 }
|
||||
|
||||
match := Xnor(isEven, isPositive)
|
||||
|
||||
fmt.Println(match(2))
|
||||
fmt.Println(match(-3))
|
||||
fmt.Println(match(3))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
// func ExamplePredicatesMix() {
|
||||
// a := Or(
|
||||
// func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||
|
||||
@@ -54,6 +54,21 @@ func TestPredicatesAndPure(t *testing.T) {
|
||||
assert.ShouldBeFalse(isNumericAndLength5("abcde"))
|
||||
}
|
||||
|
||||
func TestPredicatesNandPure(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestPredicatesNandPure")
|
||||
|
||||
isNumericAndLength5 := Nand(
|
||||
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
|
||||
func(s string) bool { return len(s) == 5 },
|
||||
)
|
||||
|
||||
assert.ShouldBeFalse(isNumericAndLength5("12345"))
|
||||
assert.ShouldBeFalse(isNumericAndLength5("1234"))
|
||||
assert.ShouldBeTrue(isNumericAndLength5("abcdef"))
|
||||
}
|
||||
|
||||
func TestPredicatesNorPure(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -74,6 +89,21 @@ func TestPredicatesNorPure(t *testing.T) {
|
||||
assert.ShouldBeFalse(match("0123456789"))
|
||||
}
|
||||
|
||||
func TestPredicatesXnorPure(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestPredicatesXnorPure")
|
||||
|
||||
isEven := func(i int) bool { return i%2 == 0 }
|
||||
isPositive := func(i int) bool { return i > 0 }
|
||||
|
||||
match := Xnor(isEven, isPositive)
|
||||
|
||||
assert.ShouldBeTrue(match(2))
|
||||
assert.ShouldBeTrue(match(-3))
|
||||
assert.ShouldBeFalse(match(3))
|
||||
}
|
||||
|
||||
func TestPredicatesMix(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -93,6 +123,12 @@ func TestPredicatesMix(t *testing.T) {
|
||||
|
||||
assert.ShouldBeFalse(c("hello!"))
|
||||
|
||||
c = Nor(a, b)
|
||||
assert.ShouldBeFalse(c("hello!"))
|
||||
k := Nor(a, b)
|
||||
assert.ShouldBeFalse(k("hello!"))
|
||||
|
||||
o := Xnor(a, b)
|
||||
assert.ShouldBeTrue(o("hello!"))
|
||||
|
||||
p := Nand(c, k)
|
||||
assert.ShouldBeTrue(p("hello!"))
|
||||
}
|
||||
|
||||
@@ -27,7 +27,15 @@ type Iterator[T any] interface {
|
||||
Next() (item T, ok bool)
|
||||
}
|
||||
|
||||
// StopIterator is an interface for stopping Iterator.
|
||||
// ResettableIterator supports to reset the iterator
|
||||
type ResettableIterator[T any] interface {
|
||||
Iterator[T]
|
||||
// Reset allows for the iteration process over a sequence to be restarted from the beginning.
|
||||
// It enables reusing the iterator for multiple traversals without needing to recreate it.
|
||||
Reset()
|
||||
}
|
||||
|
||||
// StopIterator is an interface for stopping Iterator.
|
||||
type StopIterator[T any] interface {
|
||||
Iterator[T]
|
||||
|
||||
@@ -81,8 +89,8 @@ type PrevIterator[T any] interface {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FromSlice returns an iterator over a slice of data.
|
||||
func FromSlice[T any](slice []T) Iterator[T] {
|
||||
return &sliceIterator[T]{slice: slice, index: -1}
|
||||
func FromSlice[T any](slice []T) *SliceIterator[T] {
|
||||
return &SliceIterator[T]{slice: slice, index: -1}
|
||||
}
|
||||
|
||||
func ToSlice[T any](iter Iterator[T]) []T {
|
||||
@@ -93,16 +101,16 @@ func ToSlice[T any](iter Iterator[T]) []T {
|
||||
return result
|
||||
}
|
||||
|
||||
type sliceIterator[T any] struct {
|
||||
type SliceIterator[T any] struct {
|
||||
slice []T
|
||||
index int
|
||||
}
|
||||
|
||||
func (iter *sliceIterator[T]) HasNext() bool {
|
||||
func (iter *SliceIterator[T]) HasNext() bool {
|
||||
return iter.index < len(iter.slice)-1
|
||||
}
|
||||
|
||||
func (iter *sliceIterator[T]) Next() (T, bool) {
|
||||
func (iter *SliceIterator[T]) Next() (T, bool) {
|
||||
iter.index++
|
||||
|
||||
ok := iter.index >= 0 && iter.index < len(iter.slice)
|
||||
@@ -116,7 +124,7 @@ func (iter *sliceIterator[T]) Next() (T, bool) {
|
||||
}
|
||||
|
||||
// Prev implements PrevIterator.
|
||||
func (iter *sliceIterator[T]) Prev() {
|
||||
func (iter *SliceIterator[T]) Prev() {
|
||||
if iter.index == -1 {
|
||||
panic("Next function should be called Prev")
|
||||
}
|
||||
@@ -128,7 +136,7 @@ func (iter *sliceIterator[T]) Prev() {
|
||||
}
|
||||
|
||||
// Set implements SetIterator.
|
||||
func (iter *sliceIterator[T]) Set(value T) {
|
||||
func (iter *SliceIterator[T]) Set(value T) {
|
||||
if iter.index == -1 {
|
||||
panic("Next function should be called Set")
|
||||
}
|
||||
@@ -138,52 +146,60 @@ func (iter *sliceIterator[T]) Set(value T) {
|
||||
iter.slice[iter.index] = value
|
||||
}
|
||||
|
||||
func (iter *SliceIterator[T]) Reset() {
|
||||
iter.index = -1
|
||||
}
|
||||
|
||||
// FromRange creates a iterator which returns the numeric range between start inclusive and end
|
||||
// exclusive by the step size. start should be less than end, step shoud be positive.
|
||||
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) Iterator[T] {
|
||||
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) *RangeIterator[T] {
|
||||
if end < start {
|
||||
panic("RangeIterator: start should be before end")
|
||||
} else if step <= 0 {
|
||||
panic("RangeIterator: step should be positive")
|
||||
}
|
||||
|
||||
return &rangeIterator[T]{start: start, end: end, step: step}
|
||||
return &RangeIterator[T]{start: start, end: end, step: step, current: start}
|
||||
}
|
||||
|
||||
type rangeIterator[T constraints.Integer | constraints.Float] struct {
|
||||
start, end, step T
|
||||
type RangeIterator[T constraints.Integer | constraints.Float] struct {
|
||||
start, end, step, current T
|
||||
}
|
||||
|
||||
func (iter *rangeIterator[T]) HasNext() bool {
|
||||
return iter.start < iter.end
|
||||
func (iter *RangeIterator[T]) HasNext() bool {
|
||||
return iter.current < iter.end
|
||||
}
|
||||
|
||||
func (iter *rangeIterator[T]) Next() (T, bool) {
|
||||
if iter.start >= iter.end {
|
||||
func (iter *RangeIterator[T]) Next() (T, bool) {
|
||||
if iter.current >= iter.end {
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
num := iter.start
|
||||
iter.start += iter.step
|
||||
num := iter.current
|
||||
iter.current += iter.step
|
||||
return num, true
|
||||
}
|
||||
|
||||
// FromRange creates a iterator which returns the numeric range between start inclusive and end
|
||||
// exclusive by the step size. start should be less than end, step shoud be positive.
|
||||
func FromChannel[T any](channel <-chan T) Iterator[T] {
|
||||
return &channelIterator[T]{channel: channel}
|
||||
func (iter *RangeIterator[T]) Reset() {
|
||||
iter.current = iter.start
|
||||
}
|
||||
|
||||
type channelIterator[T any] struct {
|
||||
// FromChannel creates an iterator which returns items received from the provided channel.
|
||||
// The iteration continues until the channel is closed.
|
||||
func FromChannel[T any](channel <-chan T) *ChannelIterator[T] {
|
||||
return &ChannelIterator[T]{channel: channel}
|
||||
}
|
||||
|
||||
type ChannelIterator[T any] struct {
|
||||
channel <-chan T
|
||||
}
|
||||
|
||||
func (iter *channelIterator[T]) Next() (T, bool) {
|
||||
func (iter *ChannelIterator[T]) Next() (T, bool) {
|
||||
item, ok := <-iter.channel
|
||||
return item, ok
|
||||
}
|
||||
|
||||
func (iter *channelIterator[T]) HasNext() bool {
|
||||
func (iter *ChannelIterator[T]) HasNext() bool {
|
||||
return len(iter.channel) == 0
|
||||
}
|
||||
|
||||
|
||||
@@ -50,15 +50,36 @@ func TestSliceIterator(t *testing.T) {
|
||||
assert.Equal(false, ok)
|
||||
})
|
||||
|
||||
// Reset
|
||||
t.Run("slice iterator Reset: ", func(t *testing.T) {
|
||||
iter1 := FromSlice([]int{1, 2, 3, 4})
|
||||
for i := 0; i < 4; i++ {
|
||||
item, ok := iter1.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
assert.Equal(i+1, item)
|
||||
}
|
||||
|
||||
iter1.Reset()
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
item, ok := iter1.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
assert.Equal(i+1, item)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice iterator ToSlice: ", func(t *testing.T) {
|
||||
iter := FromSlice([]int{1, 2, 3, 4})
|
||||
item, _ := iter.Next()
|
||||
assert.Equal(1, item)
|
||||
|
||||
data := ToSlice(iter)
|
||||
data := ToSlice[int](iter)
|
||||
assert.Equal([]int{2, 3, 4}, data)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestRangeIterator(t *testing.T) {
|
||||
@@ -84,6 +105,54 @@ func TestRangeIterator(t *testing.T) {
|
||||
_, ok = iter.Next()
|
||||
assert.Equal(false, ok)
|
||||
assert.Equal(false, iter.HasNext())
|
||||
|
||||
iter.Reset()
|
||||
|
||||
item, ok = iter.Next()
|
||||
assert.Equal(1, item)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
item, ok = iter.Next()
|
||||
assert.Equal(2, item)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
item, ok = iter.Next()
|
||||
assert.Equal(3, item)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
_, ok = iter.Next()
|
||||
assert.Equal(false, ok)
|
||||
assert.Equal(false, iter.HasNext())
|
||||
})
|
||||
|
||||
t.Run("range iterator reset: ", func(t *testing.T) {
|
||||
iter := FromRange(1, 4, 1)
|
||||
|
||||
item, ok := iter.Next()
|
||||
assert.Equal(1, item)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
item, ok = iter.Next()
|
||||
assert.Equal(2, item)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
iter.Reset()
|
||||
|
||||
item, ok = iter.Next()
|
||||
assert.Equal(1, item)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
item, ok = iter.Next()
|
||||
assert.Equal(2, item)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
item, ok = iter.Next()
|
||||
assert.Equal(3, item)
|
||||
assert.Equal(true, ok)
|
||||
|
||||
_, ok = iter.Next()
|
||||
assert.Equal(false, ok)
|
||||
assert.Equal(false, iter.HasNext())
|
||||
})
|
||||
|
||||
}
|
||||
@@ -93,7 +162,7 @@ func TestChannelIterator(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestRangeIterator")
|
||||
|
||||
iter := FromSlice([]int{1, 2, 3, 4})
|
||||
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
iter = FromChannel(ToChannel(ctx, iter, 0))
|
||||
|
||||
@@ -21,7 +21,7 @@ func TestMapIterator(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestMapIterator")
|
||||
|
||||
iter := FromSlice([]int{1, 2, 3, 4})
|
||||
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
|
||||
|
||||
iter = Map(iter, func(n int) int { return n / 2 })
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestFilterIterator(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestFilterIterator")
|
||||
|
||||
iter := FromSlice([]int{1, 2, 3, 4})
|
||||
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
|
||||
|
||||
iter = Filter(iter, func(n int) bool { return n < 3 })
|
||||
|
||||
@@ -47,10 +47,10 @@ func TestJoinIterator(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestJoinIterator")
|
||||
|
||||
iter1 := FromSlice([]int{1, 2})
|
||||
iter2 := FromSlice([]int{3, 4})
|
||||
var iter1 Iterator[int] = FromSlice([]int{1, 2})
|
||||
var iter2 Iterator[int] = FromSlice([]int{3, 4})
|
||||
|
||||
iter := Join(iter1, iter2)
|
||||
var iter Iterator[int] = Join(iter1, iter2)
|
||||
|
||||
item, ok := iter.Next()
|
||||
assert.Equal(1, item)
|
||||
@@ -64,7 +64,7 @@ func TestReduce(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestReduce")
|
||||
|
||||
iter := FromSlice([]int{1, 2, 3, 4})
|
||||
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4})
|
||||
sum := Reduce(iter, 0, func(a, b int) int { return a + b })
|
||||
assert.Equal(10, sum)
|
||||
}
|
||||
@@ -74,7 +74,7 @@ func TestTakeIterator(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestTakeIterator")
|
||||
|
||||
iter := FromSlice([]int{1, 2, 3, 4, 5})
|
||||
var iter Iterator[int] = FromSlice([]int{1, 2, 3, 4, 5})
|
||||
|
||||
iter = Take(iter, 3)
|
||||
|
||||
|
||||
@@ -66,28 +66,28 @@ func Percent(val, total float64, n int) float64 {
|
||||
return result
|
||||
}
|
||||
|
||||
// RoundToString round up to n decimal places.
|
||||
// RoundToString round off to n decimal places.
|
||||
// Play: https://go.dev/play/p/kZwpBRAcllO
|
||||
func RoundToString(x float64, n int) string {
|
||||
func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
||||
tmp := math.Pow(10.0, float64(n))
|
||||
x *= tmp
|
||||
x = math.Round(x)
|
||||
result := strconv.FormatFloat(x/tmp, 'f', n, 64)
|
||||
x *= T(tmp)
|
||||
r := math.Round(float64(x))
|
||||
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
|
||||
return result
|
||||
}
|
||||
|
||||
// RoundToFloat round up to n decimal places.
|
||||
// RoundToFloat round off to n decimal places.
|
||||
// Play: https://go.dev/play/p/ghyb528JRJL
|
||||
func RoundToFloat(x float64, n int) float64 {
|
||||
func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
||||
tmp := math.Pow(10.0, float64(n))
|
||||
x *= tmp
|
||||
x = math.Round(x)
|
||||
return x / tmp
|
||||
x *= T(tmp)
|
||||
r := math.Round(float64(x))
|
||||
return r / tmp
|
||||
}
|
||||
|
||||
// TruncRound round off n decimal places.
|
||||
// Play: https://go.dev/play/p/aumarSHIGzP
|
||||
func TruncRound(x float64, n int) float64 {
|
||||
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T {
|
||||
floatStr := fmt.Sprintf("%."+strconv.Itoa(n+1)+"f", x)
|
||||
temp := strings.Split(floatStr, ".")
|
||||
var newFloat string
|
||||
@@ -97,6 +97,44 @@ func TruncRound(x float64, n int) float64 {
|
||||
newFloat = temp[0] + "." + temp[1][:n]
|
||||
}
|
||||
result, _ := strconv.ParseFloat(newFloat, 64)
|
||||
return T(result)
|
||||
}
|
||||
|
||||
// FloorToFloat round down to n decimal places.
|
||||
// Play: todo
|
||||
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
||||
tmp := math.Pow(10.0, float64(n))
|
||||
x *= T(tmp)
|
||||
r := math.Floor(float64(x))
|
||||
return r / tmp
|
||||
}
|
||||
|
||||
// FloorToString round down to n decimal places.
|
||||
// Play: todo
|
||||
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
||||
tmp := math.Pow(10.0, float64(n))
|
||||
x *= T(tmp)
|
||||
r := math.Floor(float64(x))
|
||||
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
|
||||
return result
|
||||
}
|
||||
|
||||
// CeilToFloat round up to n decimal places.
|
||||
// Play: todo
|
||||
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
||||
tmp := math.Pow(10.0, float64(n))
|
||||
x *= T(tmp)
|
||||
r := math.Ceil(float64(x))
|
||||
return r / tmp
|
||||
}
|
||||
|
||||
// CeilToString round up to n decimal places.
|
||||
// Play: todo
|
||||
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
||||
tmp := math.Pow(10.0, float64(n))
|
||||
x *= T(tmp)
|
||||
r := math.Ceil(float64(x))
|
||||
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -253,7 +291,7 @@ func PointDistance(x1, y1, x2, y2 float64) float64 {
|
||||
return math.Sqrt(c)
|
||||
}
|
||||
|
||||
// IsPrimes checks if number is prime number.
|
||||
// IsPrime checks if number is prime number.
|
||||
// Play: https://go.dev/play/p/Rdd8UTHZJ7u
|
||||
func IsPrime(n int) bool {
|
||||
if n < 2 {
|
||||
@@ -351,3 +389,9 @@ func Abs[T constraints.Integer | constraints.Float](x T) T {
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
// Div returns the result of x divided by y.
|
||||
// Play: todo
|
||||
func Div[T constraints.Float | constraints.Integer](x T, y T) float64 {
|
||||
return float64(x) / float64(y)
|
||||
}
|
||||
|
||||
@@ -110,6 +110,66 @@ func ExampleTruncRound() {
|
||||
// 0.125
|
||||
}
|
||||
|
||||
func ExampleCeilToFloat() {
|
||||
result1 := CeilToFloat(3.14159, 1)
|
||||
result2 := CeilToFloat(3.14159, 2)
|
||||
result3 := CeilToFloat(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.2
|
||||
// 3.15
|
||||
// 5
|
||||
}
|
||||
|
||||
func ExampleCeilToString() {
|
||||
result1 := CeilToString(3.14159, 1)
|
||||
result2 := CeilToString(3.14159, 2)
|
||||
result3 := CeilToString(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.2
|
||||
// 3.15
|
||||
// 5.0000
|
||||
}
|
||||
|
||||
func ExampleFloorToFloat() {
|
||||
result1 := FloorToFloat(3.14159, 1)
|
||||
result2 := FloorToFloat(3.14159, 2)
|
||||
result3 := FloorToFloat(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.1
|
||||
// 3.14
|
||||
// 5
|
||||
}
|
||||
|
||||
func ExampleFloorToString() {
|
||||
result1 := FloorToString(3.14159, 1)
|
||||
result2 := FloorToString(3.14159, 2)
|
||||
result3 := FloorToString(5, 4)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// 3.1
|
||||
// 3.14
|
||||
// 5.0000
|
||||
}
|
||||
|
||||
func ExampleAverage() {
|
||||
result1 := Average(1, 2)
|
||||
result2 := RoundToFloat(Average(1.2, 1.4), 1)
|
||||
@@ -404,3 +464,17 @@ func ExampleAbs() {
|
||||
// 0.1
|
||||
// 0.2
|
||||
}
|
||||
|
||||
func ExampleDiv() {
|
||||
result1 := Div(9, 4)
|
||||
result2 := Div(1, 2)
|
||||
result3 := Div(0, 666)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
// Output:
|
||||
// 2.25
|
||||
// 0.5
|
||||
// 0
|
||||
}
|
||||
|
||||
@@ -72,6 +72,8 @@ func TestRoundToString(t *testing.T) {
|
||||
assert.Equal("0.12", RoundToString(0.124, 2))
|
||||
assert.Equal("0.13", RoundToString(0.125, 2))
|
||||
assert.Equal("0.125", RoundToString(0.125, 3))
|
||||
assert.Equal("54.321", RoundToString(54.321, 3))
|
||||
assert.Equal("17.000", RoundToString(17, 3))
|
||||
}
|
||||
|
||||
func TestTruncRound(t *testing.T) {
|
||||
@@ -79,14 +81,63 @@ func TestTruncRound(t *testing.T) {
|
||||
|
||||
assert := internal.NewAssert(t, "TestTruncRound")
|
||||
|
||||
assert.Equal(float64(0), TruncRound(0, 0))
|
||||
assert.Equal(float64(0), TruncRound(0, 1))
|
||||
assert.Equal(float64(0), TruncRound(float64(0), 0))
|
||||
assert.Equal(float64(0), TruncRound(float64(0), 1))
|
||||
assert.Equal(float32(0), TruncRound(float32(0), 0))
|
||||
assert.Equal(float32(0), TruncRound(float32(0), 1))
|
||||
assert.Equal(0, TruncRound(0, 0))
|
||||
assert.Equal(uint64(0), TruncRound(uint64(0), 1))
|
||||
assert.Equal(0.12, TruncRound(0.124, 2))
|
||||
assert.Equal(0.12, TruncRound(0.125, 2))
|
||||
assert.Equal(0.125, TruncRound(0.125, 3))
|
||||
assert.Equal(33.33, TruncRound(33.33333, 2))
|
||||
}
|
||||
|
||||
func TestFloorToFloat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestFloorToFloat")
|
||||
|
||||
assert.Equal(3.14, FloorToFloat(3.14159, 2))
|
||||
assert.Equal(3.141, FloorToFloat(3.14159, 3))
|
||||
assert.Equal(5.0, FloorToFloat(5, 4))
|
||||
assert.Equal(2.0, FloorToFloat(9/4, 2))
|
||||
}
|
||||
|
||||
func TestFloorToString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestFloorToString")
|
||||
|
||||
assert.Equal("3.14", FloorToString(3.14159, 2))
|
||||
assert.Equal("3.141", FloorToString(3.14159, 3))
|
||||
assert.Equal("5.0000", FloorToString(5, 4))
|
||||
}
|
||||
|
||||
func TestCeilToFloat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCeilToFloat")
|
||||
|
||||
assert.Equal(3.15, CeilToFloat(3.14159, 2))
|
||||
assert.Equal(3.142, CeilToFloat(3.14159, 3))
|
||||
assert.Equal(5.0, CeilToFloat(5, 4))
|
||||
assert.Equal(2.25, CeilToFloat(float32(9)/float32(4), 2))
|
||||
assert.Equal(0.15, CeilToFloat(float64(1)/float64(7), 2))
|
||||
}
|
||||
|
||||
func TestCeilToString(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestCeilToFloat")
|
||||
|
||||
assert.Equal("3.15", CeilToString(3.14159, 2))
|
||||
assert.Equal("3.142", CeilToString(3.14159, 3))
|
||||
assert.Equal("5.0000", CeilToString(5, 4))
|
||||
assert.Equal("2.25", CeilToString(float32(9)/float32(4), 2))
|
||||
assert.Equal("0.15", CeilToString(float64(1)/float64(7), 2))
|
||||
}
|
||||
|
||||
func TestAverage(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -346,4 +397,19 @@ func TestAbs(t *testing.T) {
|
||||
|
||||
assert.Equal(int64(1), Abs(int64(-1)))
|
||||
assert.Equal(float32(1), Abs(float32(-1)))
|
||||
assert.Equal(math.Inf(1), Abs(math.Inf(-1)))
|
||||
}
|
||||
|
||||
func TestDiv(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDiv")
|
||||
|
||||
assert.Equal(float64(2), Div(4, 2))
|
||||
assert.Equal(2.5, Div(5, 2))
|
||||
assert.Equal(0.5, Div(1, 2))
|
||||
assert.Equal(0.5, Div(1, 2))
|
||||
assert.Equal(math.Inf(1), Div(8, 0))
|
||||
assert.Equal(math.Inf(-1), Div(-8, 0))
|
||||
assert.Equal(true, math.IsNaN(Div(0, 0)))
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
MaximumCapacity = 1 << 31
|
||||
MaximumCapacity = math.MaxInt>>1 + 1
|
||||
Numeral = "0123456789"
|
||||
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
|
||||
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
@@ -157,7 +157,7 @@ func random(s string, length int) string {
|
||||
cache, remain = rn.Int63(), letterIdMax
|
||||
}
|
||||
// 从可用字符的字符串中随机选择一个字符
|
||||
if idx := int(cache & letterIdMask); idx < len(s) {
|
||||
if idx := int(cache & letterIdMask); idx < strLength {
|
||||
bytes[i] = s[idx]
|
||||
i--
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func RetryTimes(n uint) Option {
|
||||
}
|
||||
|
||||
// RetryWithCustomBackoff set abitary custom backoff strategy
|
||||
// todo: Add playground link
|
||||
// Play: todo
|
||||
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
|
||||
if backoffStrategy == nil {
|
||||
panic("programming error: backoffStrategy must be not nil")
|
||||
@@ -57,7 +57,7 @@ func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
|
||||
}
|
||||
|
||||
// RetryWithLinearBackoff set linear strategy backoff
|
||||
// todo: Add playground link
|
||||
// Play: todo
|
||||
func RetryWithLinearBackoff(interval time.Duration) Option {
|
||||
if interval <= 0 {
|
||||
panic("programming error: retry interval should not be lower or equal to 0")
|
||||
@@ -71,7 +71,7 @@ func RetryWithLinearBackoff(interval time.Duration) Option {
|
||||
}
|
||||
|
||||
// RetryWithExponentialWithJitterBackoff set exponential strategy backoff
|
||||
// todo: Add playground link
|
||||
// Play: todo
|
||||
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option {
|
||||
if interval <= 0 {
|
||||
panic("programming error: retry interval should not be lower or equal to 0")
|
||||
|
||||
@@ -1170,6 +1170,22 @@ func AppendIfAbsent[T comparable](slice []T, item T) []T {
|
||||
return slice
|
||||
}
|
||||
|
||||
// SetToDefaultIf sets elements to their default value if they match the given predicate.
|
||||
// It retains the positions of the elements in the slice.
|
||||
// It returns slice of T and the count of modified slice items
|
||||
// Play: todo
|
||||
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int) {
|
||||
var count int
|
||||
for i := 0; i < len(slice); i++ {
|
||||
if predicate(slice[i]) {
|
||||
var zeroValue T
|
||||
slice[i] = zeroValue
|
||||
count++
|
||||
}
|
||||
}
|
||||
return slice, count
|
||||
}
|
||||
|
||||
// KeyBy converts a slice to a map based on a callback function.
|
||||
// Play: https://go.dev/play/p/uXod2LWD1Kg
|
||||
func KeyBy[T any, U comparable](slice []T, iteratee func(item T) U) map[U]T {
|
||||
|
||||
@@ -1102,3 +1102,13 @@ func ExampleRandom() {
|
||||
// Output:
|
||||
// okk
|
||||
}
|
||||
|
||||
func ExampleSetToDefaultIf() {
|
||||
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||
modifiedStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||
fmt.Println(modifiedStrs)
|
||||
fmt.Println(count)
|
||||
// Output:
|
||||
// [ b c d ]
|
||||
// 3
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package slice
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@@ -1218,3 +1219,141 @@ func TestRandom(t *testing.T) {
|
||||
assert.Greater(len(arr), idx)
|
||||
assert.Equal(arr[idx], val)
|
||||
}
|
||||
|
||||
func TestSetToDefaultIf(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "SetToDefaultIf")
|
||||
|
||||
// Subtest for strings
|
||||
t.Run("strings", func(t *testing.T) {
|
||||
strs := []string{"a", "b", "a", "c", "d", "a"}
|
||||
actualStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
|
||||
assert.Equal([]string{"", "b", "", "c", "d", ""}, actualStrs)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for integers
|
||||
t.Run("integers", func(t *testing.T) {
|
||||
ints := []int{1, 2, 3, 2, 4, 2}
|
||||
actualInts, count := SetToDefaultIf(ints, func(i int) bool { return i == 2 })
|
||||
assert.Equal([]int{1, 0, 3, 0, 4, 0}, actualInts)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for floating-point numbers
|
||||
t.Run("floats", func(t *testing.T) {
|
||||
floats := []float64{1.1, 2.2, 3.3, 2.2, 4.4, 2.2}
|
||||
actualFloats, count := SetToDefaultIf(floats, func(f float64) bool { return f == 2.2 })
|
||||
assert.Equal([]float64{1.1, 0, 3.3, 0, 4.4, 0}, actualFloats)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for booleans
|
||||
t.Run("booleans", func(t *testing.T) {
|
||||
bools := []bool{true, false, true, true, false}
|
||||
actualBools, count := SetToDefaultIf(bools, func(b bool) bool { return b })
|
||||
assert.Equal([]bool{false, false, false, false, false}, actualBools)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for a custom type
|
||||
type customType struct {
|
||||
field string
|
||||
}
|
||||
t.Run("customType", func(t *testing.T) {
|
||||
customs := []customType{{"a"}, {"b"}, {"a"}, {"c"}}
|
||||
actualCustoms, count := SetToDefaultIf(customs, func(c customType) bool { return c.field == "a" })
|
||||
expected := []customType{{""}, {"b"}, {""}, {"c"}}
|
||||
assert.Equal(expected, actualCustoms)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for slice of integers
|
||||
t.Run("sliceOfInts", func(t *testing.T) {
|
||||
sliceOfInts := [][]int{{1, 2}, {3, 4}, {5, 6}, {1, 2}}
|
||||
actualSlice, count := SetToDefaultIf(sliceOfInts, func(s []int) bool { return reflect.DeepEqual(s, []int{1, 2}) })
|
||||
expected := [][]int{nil, {3, 4}, {5, 6}, nil}
|
||||
assert.Equal(expected, actualSlice)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for maps (simple use case)
|
||||
t.Run("mapOfStringToInts", func(t *testing.T) {
|
||||
maps := []map[string]int{{"a": 1}, {"b": 2}, {"a": 1}, {"c": 3}}
|
||||
actualMaps, count := SetToDefaultIf(maps, func(m map[string]int) bool { _, ok := m["a"]; return ok })
|
||||
expected := []map[string]int{nil, {"b": 2}, nil, {"c": 3}}
|
||||
assert.Equal(expected, actualMaps)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for pointers to integers
|
||||
t.Run("pointersToInts", func(t *testing.T) {
|
||||
one, two, three := 1, 2, 3
|
||||
pointers := []*int{&one, &two, &one, &three}
|
||||
actualPointers, count := SetToDefaultIf(pointers, func(p *int) bool { return p != nil && *p == 1 })
|
||||
expected := []*int{nil, &two, nil, &three}
|
||||
assert.Equal(expected, actualPointers)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for channels
|
||||
t.Run("channels", func(t *testing.T) {
|
||||
ch1, ch2 := make(chan int), make(chan int)
|
||||
channels := []chan int{ch1, ch2, ch1}
|
||||
actualChannels, count := SetToDefaultIf(channels, func(ch chan int) bool { return ch == ch1 })
|
||||
expected := []chan int{nil, ch2, nil}
|
||||
assert.Equal(expected, actualChannels)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for interfaces
|
||||
t.Run("interfaces", func(t *testing.T) {
|
||||
var i1, i2 interface{} = "hello", 42
|
||||
interfaces := []interface{}{i1, i2, i1}
|
||||
actualInterfaces, count := SetToDefaultIf(interfaces, func(i interface{}) bool { _, ok := i.(string); return ok })
|
||||
expected := []interface{}{nil, 42, nil}
|
||||
assert.Equal(expected, actualInterfaces)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for complex structs
|
||||
t.Run("complexStructs", func(t *testing.T) {
|
||||
type ComplexStruct struct {
|
||||
Name string
|
||||
Value int
|
||||
Data []byte
|
||||
}
|
||||
cs1, cs2 := ComplexStruct{Name: "Test", Value: 1, Data: []byte{1, 2, 3}}, ComplexStruct{Name: "Another", Value: 2, Data: []byte{4, 5, 6}}
|
||||
complexStructs := []ComplexStruct{cs1, cs2, cs1}
|
||||
actualComplexStructs, count := SetToDefaultIf(complexStructs, func(cs ComplexStruct) bool { return cs.Name == "Test" })
|
||||
expected := []ComplexStruct{{}, cs2, {}}
|
||||
assert.Equal(expected, actualComplexStructs)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
|
||||
// Subtest for uints
|
||||
t.Run("uints", func(t *testing.T) {
|
||||
uints := []uint{1, 2, 3, 2, 4, 2}
|
||||
actualUints, count := SetToDefaultIf(uints, func(u uint) bool { return u == 2 })
|
||||
assert.Equal([]uint{1, 0, 3, 0, 4, 0}, actualUints)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for float32
|
||||
t.Run("float32s", func(t *testing.T) {
|
||||
floats := []float32{1.1, 2.2, 3.3, 2.2, 4.4, 2.2}
|
||||
actualFloats, count := SetToDefaultIf(floats, func(f float32) bool { return f == 2.2 })
|
||||
assert.Equal([]float32{1.1, 0, 3.3, 0, 4.4, 0}, actualFloats)
|
||||
assert.Equal(3, count)
|
||||
})
|
||||
|
||||
// Subtest for []byte
|
||||
t.Run("byteSlices", func(t *testing.T) {
|
||||
bytes := [][]byte{{'a', 'b'}, {'c', 'd'}, {'a', 'b'}}
|
||||
actualBytes, count := SetToDefaultIf(bytes, func(b []byte) bool { return string(b) == "ab" })
|
||||
expected := [][]byte{nil, {'c', 'd'}, nil}
|
||||
assert.Equal(expected, actualBytes)
|
||||
assert.Equal(2, count)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
@@ -594,3 +595,25 @@ func SubInBetween(str string, start string, end string) string {
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// HammingDistance calculates the Hamming distance between two strings.
|
||||
// The Hamming distance is the number of positions at which the corresponding symbols are different.
|
||||
// This func returns an error if the input strings are of unequal lengths.
|
||||
// Play: todo
|
||||
func HammingDistance(a, b string) (int, error) {
|
||||
if len(a) != len(b) {
|
||||
return -1, errors.New("a length and b length are unequal")
|
||||
}
|
||||
|
||||
ar := []rune(a)
|
||||
br := []rune(b)
|
||||
|
||||
var distance int
|
||||
for i, codepoint := range ar {
|
||||
if codepoint != br[i] {
|
||||
distance++
|
||||
}
|
||||
}
|
||||
|
||||
return distance, nil
|
||||
}
|
||||
|
||||
@@ -667,3 +667,16 @@ func ExampleSubInBetween() {
|
||||
// abc
|
||||
// bc
|
||||
}
|
||||
|
||||
func ExampleHammingDistance() {
|
||||
|
||||
result, _ := HammingDistance("abc", "def")
|
||||
fmt.Println(result)
|
||||
|
||||
result, _ = HammingDistance("name", "namf")
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
// 1
|
||||
}
|
||||
|
||||
@@ -581,3 +581,26 @@ func TestSubInBetween(t *testing.T) {
|
||||
assert.Equal("", SubInBetween(str, "a", ""))
|
||||
assert.Equal("", SubInBetween(str, "a", "f"))
|
||||
}
|
||||
|
||||
func TestHammingDistance(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "HammingDistance")
|
||||
|
||||
hd := func(a, b string) int {
|
||||
c, _ := HammingDistance(a, b)
|
||||
return c
|
||||
}
|
||||
|
||||
assert.Equal(0, hd(" ", " "))
|
||||
assert.Equal(1, hd(" ", "c"))
|
||||
assert.Equal(1, hd("a", "d"))
|
||||
assert.Equal(1, hd("a", " "))
|
||||
assert.Equal(1, hd("a", "f"))
|
||||
|
||||
assert.Equal(0, hd("", ""))
|
||||
assert.Equal(-1, hd("abc", "ab"))
|
||||
assert.Equal(3, hd("abc", "def"))
|
||||
assert.Equal(-1, hd("kitten", "sitting"))
|
||||
assert.Equal(1, hd("ö", "ü"))
|
||||
assert.Equal(0, hd("日本語", "日本語"))
|
||||
assert.Equal(3, hd("日本語", "語日本"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user