mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-22 21:32:26 +08:00
Compare commits
30 Commits
4a539a23c8
...
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 |
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 中给出描述信息。
|
||||
|
||||
|
||||
60
README.md
60
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>
|
||||
|
||||
@@ -721,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>
|
||||
|
||||
@@ -783,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
|
||||
@@ -931,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)]
|
||||
@@ -970,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>
|
||||
|
||||
@@ -1139,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>
|
||||
|
||||
@@ -1364,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>
|
||||
@@ -1609,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())
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
|
||||
// New create a instance of set from given values.
|
||||
func New[T comparable](items ...T) Set[T] {
|
||||
set := make(Set[T])
|
||||
set := make(Set[T], len(items))
|
||||
set.Add(items...)
|
||||
return set
|
||||
}
|
||||
|
||||
// FromSlice create a set from given slice.
|
||||
func FromSlice[T comparable](items []T) Set[T] {
|
||||
set := make(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 := New[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
|
||||
@@ -163,7 +161,7 @@ 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 := New[T]()
|
||||
|
||||
@@ -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,6 +1,8 @@
|
||||
package datastructure
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
@@ -260,3 +262,65 @@ func TestEachWithBreak(t *testing.T) {
|
||||
// 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
|
||||
}
|
||||
```
|
||||
@@ -40,6 +40,9 @@ import (
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
- [Pop](#Pop)
|
||||
- [ToSlice](#ToSlice)
|
||||
- [ToSortedSlice](#ToSortedSlice)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -52,7 +55,7 @@ import (
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
type Set[T comparable] map[T]bool
|
||||
type Set[T comparable] map[T]struct{}
|
||||
func New[T comparable](items ...T) Set[T]
|
||||
```
|
||||
|
||||
@@ -98,9 +101,10 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <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>
|
||||
|
||||
@@ -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}]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -50,6 +50,8 @@ import (
|
||||
- [WriteStringToFile](#WriteStringToFile)
|
||||
- [WriteBytesToFile](#WriteBytesToFile)
|
||||
- [ReadFile](#ReadFile)
|
||||
- [ChunkRead](#ChunkRead)
|
||||
- [ParallelChunkRead](#ParallelChunkRead)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -955,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
|
||||
}
|
||||
```
|
||||
@@ -39,6 +39,8 @@ import (
|
||||
- [Nor](#Nor)
|
||||
- [Xnor](#Xnor)
|
||||
- [Nand](#Nand)
|
||||
- [AcceptIf](#AcceptIf)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -638,4 +640,55 @@ func main() {
|
||||
// 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
|
||||
}
|
||||
```
|
||||
@@ -41,6 +41,9 @@ import (
|
||||
- [Intersection](#Intersection)
|
||||
- [SymmetricDifference](#SymmetricDifference)
|
||||
- [Minus](#Minus)
|
||||
- [Pop](#Pop)
|
||||
- [ToSlice](#ToSlice)
|
||||
- [ToSortedSlice](#ToSortedSlice)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -53,7 +56,7 @@ import (
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
type Set[T comparable] map[T]bool
|
||||
type Set[T comparable] map[T]struct{}
|
||||
func New[T comparable](items ...T) Set[T]
|
||||
```
|
||||
|
||||
@@ -99,9 +102,10 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <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>
|
||||
|
||||
@@ -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}]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -50,6 +50,8 @@ import (
|
||||
- [WriteStringToFile](#WriteStringToFile)
|
||||
- [WriteBytesToFile](#WriteBytesToFile)
|
||||
- [ReadFile](#ReadFile)
|
||||
- [ChunkRead](#ChunkRead)
|
||||
- [ParallelChunkRead](#ParallelChunkRead)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -961,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
|
||||
}
|
||||
```
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
- [Nor](#Nor)
|
||||
- [Xnor](#Xnor)
|
||||
- [Nand](#Nand)
|
||||
- [AcceptIf](#AcceptIf)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -638,4 +639,57 @@ func main() {
|
||||
// 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 中给出描述信息。
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
)
|
||||
@@ -866,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
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
@@ -566,3 +568,54 @@ func TestCopyDir(t *testing.T) {
|
||||
|
||||
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])
|
||||
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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