1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-04 21:02:27 +08:00

Compare commits

..

112 Commits

Author SHA1 Message Date
dudaodong
036847577d release v2.3.0 2024-03-05 14:57:16 +08:00
dudaodong
d21edd1cde doc: add doc for retry package 2024-03-05 14:38:42 +08:00
dudaodong
c58c50327c doc: add doc for ChunkRead and ParallelChunkRead 2024-03-05 11:42:03 +08:00
dudaodong
9bfdc686f8 doc: update contribution_guide file 2024-03-05 10:47:51 +08:00
dudaodong
5ca8f6ef6f doc: update doc 2024-03-05 10:45:40 +08:00
dudaodong
a54d4c79a0 doc: update doc for mathutil package 2024-03-04 19:52:49 +08:00
Cannian
f7b54986aa feat(mathutil): add DIv, FloorToFloat, FloorToString, CeilToFloat, CeilToString (#199) 2024-03-04 17:40:31 +08:00
dudaodong
92fae4273b doc: update doc for new added functions 2024-03-04 16:42:59 +08:00
dudaodong
0d29f5437a doc: update doc for new added functions 2024-03-04 16:41:54 +08:00
dudaodong
e95d7c82cd doc: update doc for function package 2024-03-04 10:19:39 +08:00
donutloop
e138043289 Function: AcceptIf (#198)
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.
2024-03-04 10:00:43 +08:00
dudaodong
aabfcb7bde doc: update doc for HammingDistance 2024-03-03 21:55:27 +08:00
donutloop
0b5e884371 Strutil: HammingDistance func (#197)
* Strutil: HammingDistance func

The Hamming distance is the number of positions at which the corresponding symbols are different

* Add hamming distance doc
2024-03-03 21:48:34 +08:00
dudaodong
3d1bd08434 doc: update doc for new functions 2024-03-01 22:38:57 +08:00
dudaodong
a62ad71791 Merge branch 'rc' into v2 2024-03-01 21:45:08 +08:00
Cannian
c02c4f813b refactor(mathutil): round related method support generics (#195) 2024-03-01 16:04:23 +08:00
pigwantacat
235d2f2486 修复MAXIMUM_CAPACITY超过int最大值 (#196)
* refactor:refactor random function

* fix:fix random function

---------

Co-authored-by: zhuhebin <zhuhebin@fengtaisec.com>
2024-03-01 15:52:49 +08:00
dudaodong
e9380a3d9f fix: fix unused parameters vet issue 2024-03-01 10:05:28 +08:00
Cannian
81d13c2f1a feat(convertor): add ToBase64 related method (#194) 2024-02-29 19:22:32 +08:00
dudaodong
7290296849 update Contributing Guide 2024-02-29 11:49:34 +08:00
dudaodong
8a8460a592 Merge branch 'rc' into v2 2024-02-29 11:34:53 +08:00
dudaodong
7a98c431d3 Merge branch 'main' of github.com:duke-git/lancet 2024-02-29 11:32:44 +08:00
colorcrow
606d887230 增加ParallelChunkRead方法,分块并发读取超大文本 (#192) 2024-02-29 11:32:10 +08:00
donutloop
473f9c9f3e Iterator: general refactoring and reset method (#193)
Feature

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.

Refactoring

It is a idiomatic practice to design functions and methods to return concrete struct types.
This approach promotes flexibility and decoupling, allowing the calling code to work with any implementation that satisfies the interface
2024-02-29 11:30:12 +08:00
dudaodong
9ff3d0e79c create rc branch for release candidate 2024-02-29 11:25:20 +08:00
dudaodong
5db1d07d6d Merge branch 'main' into v2 2024-02-28 15:55:59 +08:00
Cannian
6c6d14828a feat(set, doc): add ToSlice,ToSortedSlice method,fix doc (#189) 2024-02-28 15:43:34 +08:00
donutloop
0e1593c67b Slice: Add SetToDefaultIf (#187)
SetToDefaultIf sets elements to their zero 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
2024-02-28 11:27:23 +08:00
cannian1
6c7f38d8b3 feat: more readable panic (#183) 2024-02-27 10:38:08 +08:00
donutloop
069812e0ee Hashmap: Add FilterByValue (#184)
The FilterByValue function is a method defined on the HashMap type. It generates a new HashMap
containing only the elements that satisfy a specified condition,
as determined by a predicate function applied to each element's value.

Note: Will add later doc
2024-02-27 10:36:26 +08:00
dudaodong
4a539a23c8 doc: add doc for Xnor and Nand in function package 2024-02-26 10:10:37 +08:00
dudaodong
0b1dab0399 Merge branch 'main' into v2 2024-02-26 10:02:00 +08:00
donutloop
805e2543d0 Add functional predicate NAND (#182)
Add new function, NAND, designed to create a composed predicate representing the logical NAND operation
applied to a list of predicates. The NAND operation is a logical operation
that returns true only if all perdicate result in false otherwise false
2024-02-26 09:58:19 +08:00
dudaodong
a3d518da76 remove test_src directory 2024-02-25 20:29:49 +08:00
dudaodong
e3e2d8394c Merge branch 'v2' of github.com:duke-git/lancet into v2 2024-02-25 20:25:44 +08:00
yunxuan
0eeaa06055 feat(fileutil): add CopyDir func (#180)
* add fileutil.CopyDir

* remove debug code
2024-02-25 20:25:00 +08:00
donutloop
a43bc554ee Add functional predicate XNOR (#181)
Add new function, Xnor, designed to create a composed predicate representing
the logical Exclusive NOR (XNOR) operation applied to a list of predicates.
The XNOR operation is a logical operation that returns true only
if all operands have the same boolean value
2024-02-25 20:24:47 +08:00
dudaodong
aebab7c944 refactor: break change, rename constructor of set (NewSet->New, NewSetFromSlice->FromSlice) 2024-02-25 09:32:32 +08:00
dudaodong
665bad4ca3 doc: update doc for IndexOfFunc and LastIndexOfFunc 2024-02-25 09:25:07 +08:00
donutloop
e4901e99e9 Fix optional doc links (#179) 2024-02-24 18:12:44 +08:00
donutloop
4277e8eca5 CopyOnWriteList add IndexOfFunc and LastIndexOfFunc (#178)
Allow users to apply functional predicates alongside 'index of' and 'last index of' search methods in this specialized list variant
2024-02-24 17:53:58 +08:00
donutloop
fdc93c8cc7 Change naming (#177)
Utilize terminology from the Go SDK rather than introducing novel terms to describe concepts.
2024-02-24 17:25:31 +08:00
donutloop
860a499f98 Add custom backoff setter (#176)
Users should have the capability to customize the backoff pattern and accordingly adjust it within the retry mechanism.
2024-02-23 10:04:38 +08:00
dudaodong
2e1c2276a5 test: add test coverageg 2024-02-22 14:39:53 +08:00
donutloop
d367397dab Add exponential With jitter backoff (#174)
* Add exponential With jitter backoff

Adds exponential + jitter retry policy. To enable drastic slow down of sending out requests to any external system.

Jitter in computational contexts refers to the addition of a small random variation to a value
to break the symmetric patterns

* Retry with exp: Allow shift for all multiple of 2
2024-02-22 10:39:45 +08:00
dudaodong
66fd8cf651 fix: fix go vet issue 2024-02-21 11:14:19 +08:00
dudaodong
a6be1828b9 doc: add doc and example for predicate logic of function package 2024-02-21 11:13:47 +08:00
dudaodong
8f5d297572 . 2024-02-21 11:11:52 +08:00
dudaodong
a1a4fdc598 doc: update doc for optional 2024-02-21 10:38:26 +08:00
dudaodong
1610076d22 Merge branch 'main' into v2 2024-02-21 10:21:42 +08:00
donutloop
cacbf97223 Add Retry backoff policy (#173)
The aim of this policy is to enable the configuration of various types of backoff mathematical curves. Should this modification be deemed suitable,
I will proceed to implement an exponential curve backoff policy and set of custom backoff policy
Warning: It's major break
2024-02-21 10:20:24 +08:00
donutloop
cd156dba5f Add functional nor predicate (#172) 2024-02-21 10:05:54 +08:00
dudaodong
3a71a8697d fix: fix issue #169 2024-02-20 17:29:32 +08:00
dudaodong
c88fd3db86 fix: fix go vet issue, method Unwrap() []error 2024-02-20 11:41:58 +08:00
dudaodong
27d19d1717 fix: rename Seek to SeekOffset fix go vet check issue 2024-02-20 11:39:41 +08:00
dudaodong
da24bae6b4 doc: add doc for Optional type 2024-02-20 11:22:39 +08:00
donutloop
3cd9d6b68c Add functional predicate (#171)
Enable the execution of assertion functions in a functional manner.
2024-02-20 09:55:39 +08:00
dudaodong
874d09f331 refactor: make stream struct exported 2024-02-19 15:55:08 +08:00
dudaodong
fdf251ac98 add govet check to github action file 2024-02-19 15:50:19 +08:00
dudaodong
7ec2533b7a feat: add MapToStruct 2024-02-19 13:50:06 +08:00
dudaodong
9fd0603f4a fix: fix copylocks warning in Optional struct methods 2024-02-19 10:22:28 +08:00
dudaodong
9f7b416a8d Merge branch 'main' into v2 2024-02-19 10:00:21 +08:00
donutloop
bf4b2b5fd6 Add optional (#170)
* Add optional

Wrapper container with easy to understand helper methods

* Add test and rewrite test

* Add panic test

* Add TestOrElseGetHappyPath
2024-02-19 09:59:42 +08:00
dudaodong
22af59565e fix: fix issue #168 2024-02-06 16:59:01 +08:00
dudaodong
f9e047f190 feat: add SubInBetween 2024-02-06 16:47:30 +08:00
dudaodong
fa298b740d add playground demo 2024-02-01 10:41:09 +08:00
dudaodong
6d4fc981b6 release v2.2.9 2024-02-01 10:03:26 +08:00
dudaodong
4c21fe700c refactor: update StringToBytes logic 2024-01-30 10:46:55 +08:00
pigwantacat
b7370e8ef8 refactor:refactor random function (#164)
Co-authored-by: zhuhebin <zhuhebin@fengtaisec.com>
2024-01-30 10:00:14 +08:00
dudaodong
38920e3be6 fix: fix issue #162 2024-01-29 11:00:50 +08:00
dudaodong
a630a7cda9 refactor: remove struct_internal.go 2024-01-29 10:02:36 +08:00
dudaodong
66dfd9c4fd refactor: refact Comma function 2024-01-25 16:45:17 +08:00
dudaodong
be62aaac9b refactor: update signature of WriteMapsToCsv function
g
2024-01-24 11:54:42 +08:00
dudaodong
e0c9ccbce3 doc: add DeleteRange 2024-01-23 17:27:28 +08:00
dudaodong
d2d1e5a055 feat: refact DeleteAt and add DeleteRange function 2024-01-22 15:34:35 +08:00
dudaodong
bbc58c7e46 fix: fix the map key order issue of WriteMapsToCsv 2024-01-22 11:10:02 +08:00
dudaodong
ac2ecceaec refactor: refact reduce function 2024-01-03 10:46:28 +08:00
dudaodong
a06bb8ee6a fix: fix unit test for WriteMapsToCsv 2024-01-01 18:32:41 +08:00
dudaodong
27b5702fd3 fix: fix unit test for WriteMapsToCsv 2024-01-01 17:51:32 +08:00
dudaodong
b2c3fa0ab8 fix: fix golint issue 2024-01-01 17:43:11 +08:00
dudaodong
4afc838937 doc: add doc for WriteMapsToCsv 2024-01-01 17:40:32 +08:00
colorcrow
3482f80d1c WriteCsvFile自定义分割符,增加map切片写入csv,增加追踪函数运行时间的函数 (#157)
* WriteCsvFile now support custom delimiter,Add write map slice to csv

* add trace func time
2024-01-01 16:54:04 +08:00
suacrbah
565f2893b9 feat: add support for seeking and read one line at a time from file (#158)
* feat: add support for seeking and read one line at a time from file

* feat: add support for calculating folder total size

---------

Co-authored-by: Suacrbah <5744580+Suacrbah@user.noreply.gitee.com>
2024-01-01 16:50:54 +08:00
dudaodong
1b1b10d0ee merge main branch 2023-12-29 10:17:04 +08:00
Gregory Koganovskiy
c5c3888ffc Fix typos in README (#154) 2023-12-29 10:12:29 +08:00
duckjiangwei
11214986cc add new func & docs error repair (#153)
* feat: add method IsTargetType()

* docs:document error repair
2023-12-28 10:27:32 +08:00
dudaodong
0bc7b83e59 Merge branch 'main' into v2 2023-12-21 10:03:35 +08:00
duckjiangwei
6225418074 improvement:optimize bubble sort (#152) 2023-12-21 10:02:23 +08:00
dudaodong
ddd265de78 fix: fix comment error 2023-12-19 15:36:07 +08:00
dudaodong
80e48f06ca refactor: rename lancetconstraints package name to constraints 2023-12-16 19:29:06 +08:00
dudaodong
0b976e9a4c fix: fix failed unit test 2023-12-11 15:45:02 +08:00
dudaodong
96320069f4 doc: update for release v2.2.8 2023-12-11 15:30:33 +08:00
dudaodong
c5297ec329 fix: fix failed unit test 2023-12-11 15:04:54 +08:00
dudaodong
aa4b61ff85 release v2.2.8 2023-12-11 14:59:20 +08:00
dudaodong
7dbd7002a3 doc: update doc for new release 2023-12-11 14:58:28 +08:00
dudaodong
a995db445a doc: update doc for random package 2023-12-08 11:57:39 +08:00
dudaodong
6e5b67bee7 feat: add RandFloat and RandFloats 2023-12-07 19:41:45 +08:00
dudaodong
52b8ea8166 doc: update doc for random package 2023-12-07 19:13:05 +08:00
dudaodong
6fe8a9efe7 feat: add RandSymbolChar 2023-12-07 17:56:12 +08:00
dudaodong
dcef06e9da fix: fix dns regex matcher 2023-12-07 09:54:03 +08:00
dudaodong
8f410bf9cb Merge branch 'main' into v2 2023-12-01 16:43:36 +08:00
dengrandpa
9cd6eb4ddf fix(validator): [IsChineseIdNum] fix 身份证效验 新增 省份、生日、效验码效验,修改ContainLower/IsNumber 注释错误 (#149)
* fix: 修改 README_zh-CN.md datetime 重复 IsLeapYear问题

* fix: 修改 README_zh-CN.md 18. slice 回到目录

* fix(validator): [IsChineseIdNum] fix 身份证效验 新增 省份、生日、效验码效验,修改ContainLower/IsNumber 注释错误

* fix(validator): [IsChineseIdNum] fix 修改测试

---------

Co-authored-by: dengjiaxiang <19181730103@189.cn>
Co-authored-by: dengrandpa <dengrandpapa@outlook.com>
2023-12-01 16:42:35 +08:00
dudaodong
bf581162ee Merge branch 'main' into v2 2023-11-29 11:46:09 +08:00
dengrandpa
bd984fa378 fix: 修改 README_zh-CN.md 文档错误 (#147)
* fix: 修改 README_zh-CN.md datetime 重复 IsLeapYear问题

* fix: 修改 README_zh-CN.md 18. slice 回到目录

---------

Co-authored-by: dengjiaxiang <19181730103@189.cn>
2023-11-29 11:43:54 +08:00
dudaodong
d7f23e2dee Merge branch 'main' into v2 2023-11-22 17:20:17 +08:00
o98k
3802c715c3 [feature]<slice>: support random item (#146)
Signed-off-by: o98k-ok <hggend@gmail.com>
2023-11-22 16:51:37 +08:00
dudaodong
4b12173f24 Merge branch 'main' into v2 2023-11-16 15:28:34 +08:00
guangwu
31c618c187 chore: use bytes.Equal instead (#144) 2023-11-16 15:26:20 +08:00
dudaodong
6497b321b0 refactor: clean code 2023-10-30 17:55:18 +08:00
dudaodong
bda78201f5 doc: update readme file 2023-10-28 17:02:24 +08:00
dudaodong
0753ea2801 feat: add context for httpClient SendRequest function 2023-10-18 19:13:34 +08:00
dudaodong
e25b53712b doc: add play ground demo 2023-10-08 11:19:44 +08:00
118 changed files with 8431 additions and 828 deletions

View File

@@ -3,11 +3,11 @@ on:
push:
branches:
- main
# - v2
- rc
pull_request:
branches:
- main
# - v2
- rc
jobs:
build:
runs-on: ubuntu-latest
@@ -17,8 +17,10 @@ jobs:
fetch-depth: 2
- uses: actions/setup-go@v2
with:
go-version: "1.18"
go-version: "1.20"
- name: Run coverage
run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic
- name: Run govet
run: go vet -v ./...
- name: Upload coverage to Codecov
run: bash <(curl -s https://codecov.io/bash)

View File

@@ -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.

View File

@@ -30,7 +30,7 @@ Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代
- 提交 PR 前请执行单元测试命令go test -v ./...,确保所有单元测试任务通过。
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
- 确保 PR 是提交到 `rc` 分支,而不是其他分支。
- 如果是修复 bug请在 PR 中给出描述信息。

212
README.md
View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.2.7-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.0-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
@@ -21,7 +21,7 @@
## <a href="https://www.golancet.cn/en/" target="_blank"> Website</a> | [简体中文](./README_zh-CN.md)
## Feature
## Features
- 👏 Comprehensive, efficient and reusable.
- 💪 600+ go util functions, support string, slice, datetime, net, crypt...
@@ -32,13 +32,13 @@
### Note:
1. <b>For users who use go1.18 and above, it is recommended to install lancet v2.x.x. Cause in v2.x.x all functions was rewriten with generics of go1.18.</b>
1. <b>For users who use go1.18 and above, it is recommended to install lancet v2.x.x. Cause in v2.x.x all functions were rewritten with generics of go1.18.</b>
```go
go 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
@@ -201,10 +201,10 @@ import "github.com/duke-git/lancet/v2/concurrency"
- **<big>OrDone</big>** : read a channel into another channel, will close until cancel context.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#OrDone)]
[[play](https://go.dev/play/p/lm_GoS6aDjo)]
- **<big>Repeat</big>** : create channel, put values into the channel repeatly until cancel the context.
- **<big>Repeat</big>** : create channel, put values into the channel repeatedly until cancel the context.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#Repeat)]
[[play](https://go.dev/play/p/k5N_ALVmYjE)]
- **<big>RepeatFn</big>** : create a channel, excutes fn repeatly, and put the result into the channel, until close context.
- **<big>RepeatFn</big>** : create a channel, executes fn repeatedly, and put the result into the channel, until close context.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/concurrency.md#RepeatFn)]
[[play](https://go.dev/play/p/4J1zAWttP85)]
- **<big>Take</big>** : create a channel whose values are taken from another channel with limit number.
@@ -247,7 +247,7 @@ import "github.com/duke-git/lancet/v2/condition"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/condition.md#TernaryOperator)]
[[play](https://go.dev/play/p/ElllPZY0guT)]
<h3 id="convertor"> 5. Convertor package contains some functions for data convertion. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a> </h3>
<h3 id="convertor"> 5. Convertor package contains some functions for data conversion. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a> </h3>
```go
import "github.com/duke-git/lancet/v2/convertor"
@@ -318,6 +318,14 @@ import "github.com/duke-git/lancet/v2/convertor"
- **<big>GbkToUtf8</big>** : converts GBK encoding data to utf8 encoding data.
[[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.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -453,10 +461,14 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/uef0q1fz53I)]
- **<big>GenerateRsaKeyPair</big>** : creates rsa private and public key.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#GenerateRsaKeyPair)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
- **<big>RsaEncryptOAEP</big>** : encrypts the given data with RSA-OAEP.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaEncryptOAEP)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
- **<big>RsaDecryptOAEP</big>** : decrypts the data with RSA-OAEP
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/cryptor.md#RsaDecryptOAEP)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
<h3 id="datetime"> 7. Datetime package supports date and time format and compare. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -592,7 +604,7 @@ import "github.com/duke-git/lancet/v2/datetime"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datetime.md#TimestampNano)]
[[play](https://go.dev/play/p/A9Oq_COrcCF)]
<h3 id="datastructure"> 8. Datastructure package constains some common data structure. eg. list, linklist, stack, queue, set, tree, graph. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
<h3 id="datastructure"> 8. Datastructure package contains some common data structure. eg. list, linklist, stack, queue, set, tree, graph. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
import list "github.com/duke-git/lancet/v2/datastructure/list"
@@ -604,6 +616,7 @@ import set "github.com/duke-git/lancet/v2/datastructure/set"
import tree "github.com/duke-git/lancet/v2/datastructure/tree"
import heap "github.com/duke-git/lancet/v2/datastructure/heap"
import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
import optional "github.com/duke-git/lancet/v2/datastructure/optional"
```
#### Structure list:
@@ -626,6 +639,9 @@ import hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/heap.md)]
- **<big>Hashmap</big>** : hash map structure.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/hashmap.md)]
- **<big>Optional</big>** : Optional container.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/datastructure/optional.md)]
<h3 id="fileutil"> 9. Fileutil package implements some basic functions for file operations. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -702,7 +718,9 @@ import "github.com/duke-git/lancet/v2/fileutil"
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
- **<big>WriteCsvFile</big>** : write content to target csv file.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteCsvFile)]
[[play](https://go.dev/play/p/dAXm58Q5U1o)]
- **<big>WriteMapsToCsv</big>** : write slice of map to csv file.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteMapsToCsv)]
[[play](https://go.dev/play/p/umAIomZFV1c)]
- **<big>WriteBytesToFile</big>** : write bytes to target file.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteBytesToFile)]
[[play](https://go.dev/play/p/s7QlDxMj3P8)]
@@ -711,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. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -750,10 +772,10 @@ import "github.com/duke-git/lancet/v2/function"
#### Function list:
- **<big>After</big>** : return a function that invokes passed funcation once the returned function is called more than n times.
- **<big>After</big>** : return a function that invokes passed function once the returned function is called more than n times.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#After)]
[[play](https://go.dev/play/p/eRD5k2vzUVX)]
- **<big>Before</big>** : return a function that invokes passed funcation once the returned function is called less than n times
- **<big>Before</big>** : return a function that invokes passed function once the returned function is called less than n times
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Before)]
[[play](https://go.dev/play/p/0HqUDIFZ3IL)]
- **<big>CurryFn</big>** : make a curry function.
@@ -773,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>Watcher</big>** : Watcher is used for record code excution time. can start/stop/reset the watch timer. get the elapsed time of function execution.
- **<big>AcceptIf</big>** : returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#AcceptIf)]
- **<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.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
```go
@@ -789,7 +825,7 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>MapTo</big>** : quick map any value to struct or any base type.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#MapTo)]
[[play](https://go.dev/play/p/4K7KBEPgS5M)]
- **<big>ForEach</big>** : executes iteratee funcation for every key and value pair in map.
- **<big>ForEach</big>** : executes iteratee function for every key and value pair in map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ForEach)]
[[play](https://go.dev/play/p/OaThj6iNVXK)]
- **<big>Filter</big>** : iterates over map, return a new map contains all key and value pairs pass the predicate function.
@@ -807,7 +843,7 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>OmitByKeys</big>** : the opposite of FilterByKeys, extracts all the map elements which keys are not omitted.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OmitByKeys)]
[[play](https://go.dev/play/p/jXGrWDBfSRp)]
- **<big>OmitByValues</big>** : the opposite of FilterByValues. remov all elements whose value are in the give slice.
- **<big>OmitByValues</big>** : the opposite of FilterByValues. remove all elements whose value are in the give slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#OmitByValues)]
[[play](https://go.dev/play/p/XB7Y10uw20_U)]
- **<big>Intersect</big>** : iterates over maps, return a new map of key and value pairs in all given maps.
@@ -921,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)]
@@ -957,9 +1001,12 @@ import "github.com/duke-git/lancet/v2/mathutil"
- **<big>Sum</big>** : return sum of passed numbers.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)]
[[play](https://go.dev/play/p/1To2ImAMJA7)]
- **<big>Abs</big>** : returns the absolute value of param nubmer.
- **<big>Abs</big>** : returns the absolute value of param number.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)]
[[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. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1008,7 +1055,7 @@ import "github.com/duke-git/lancet/v2/netutil"
- **<big>DecodeResponse</big>** : decode http response into target object.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#DecodeResponse)]
[[play](https://go.dev/play/p/jUSgynekH7G)]
- **<big>StructToUrlValues</big>** : convert struct to url valuse.
- **<big>StructToUrlValues</big>** : convert struct to url values.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/netutil.md#StructToUrlValues)]
[[play](https://go.dev/play/p/pFqMkM40w9z)]
- **<big>HttpGet<sup>deprecated</sup></big>** : send http get request.
@@ -1093,6 +1140,18 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>RandUniqueIntSlice</big>** : generate a slice of random int of length n that do not repeat.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandUniqueIntSlice)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandSymbolChar</big>** : Generate a random symbol char of specified length.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandSymbolChar)]
[[play](https://go.dev/play/p/Im6ZJxAykOm)]
- **<big>RandFloat</big>** : Generate a random float64 number between [min, max) with specific precision.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloat)]
[[play](https://go.dev/play/p/zbD_tuobJtr)]
- **<big>RandFloats</big>** : Generate a slice of random float64 numbers of length n that do not repeat.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/random.md#RandFloats)]
[[play](https://go.dev/play/p/I3yndUQ-rhh)]
<h3 id="retry"> 17. Retry package is for executing a function repeatedly until it was successful or canceled by the context. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1117,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. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1141,7 +1210,7 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Chunk</big>** : creates a slice of elements split into groups the length of size.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Chunk)]
[[play](https://go.dev/play/p/b4Pou5j2L_C)]
- **<big>Compact</big>** : creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey.
- **<big>Compact</big>** : creates an slice with all falsy values removed. The values false, nil, 0, and "" are falsy.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Compact)]
[[play](https://go.dev/play/p/pO5AnxEr3TK)]
- **<big>Concat</big>** : creates a new slice concatenating slice with any additional slices.
@@ -1162,9 +1231,12 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>DifferenceWith</big>** : accepts comparator which is invoked to compare elements of slice to values.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DifferenceWith)]
[[play](https://go.dev/play/p/v2U2deugKuV)]
- **<big>DeleteAt</big>** : delete the element of slice from specific start index to end index - 1.
- **<big>DeleteAt</big>** : delete the element of slice at index.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteAt)]
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
[[play](https://go.dev/play/p/800B1dPBYyd)]
- **<big>DeleteRange</big>** : delete the element of slice from start index to end indexexclude).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteRange)]
[[play](https://go.dev/play/p/945HwiNrnle)]
- **<big>Drop</big>** : drop n elements from the start of a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Drop)]
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
@@ -1222,7 +1294,7 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>GroupBy</big>** : iterate over elements of the slice, each element will be group by criteria, returns two slices.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#GroupBy)]
[[play](https://go.dev/play/p/QVkPxzPR0iA)]
- **<big>GroupWith</big>** : return a map composed of keys generated from the resultults of running each element of slice thru iteratee.
- **<big>GroupWith</big>** : return a map composed of keys generated from the results of running each element of slice thru iteratee.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#GroupWith)]
[[play](https://go.dev/play/p/ApCvMNTLO8a)]
- **<big>IntSlice<sup>deprecated</sup></big>** : convert param to int slice.
@@ -1336,6 +1408,12 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Partition</big>** : partition all slice elements with the evaluation of the given predicate functions.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Partition)]
[[play](https://go.dev/play/p/lkQ3Ri2NQhV)]
- **<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. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1357,7 +1435,7 @@ import "github.com/duke-git/lancet/v2/stream"
- **<big>FromRange</big>** : creates a number stream from start to end. both start and end are included. [start, end]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FromRange)]
[[play](https://go.dev/play/p/9Ex1-zcg-B-)]
- **<big>Generate</big>** : creates a stream where each element is generated by the provided generater function.
- **<big>Generate</big>** : creates a stream where each element is generated by the provided generator function.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Generate)]
[[play](https://go.dev/play/p/rkOWL1yA3j9)]
- **<big>Concat</big>** : creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream.
@@ -1402,10 +1480,10 @@ import "github.com/duke-git/lancet/v2/stream"
- **<big>FindLast</big>** : returns the last element of this stream and true, or zero value and false if the stream is empty.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#FindLast)]
[[play](https://go.dev/play/p/WZD2rDAW-2h)]
- **<big>Max</big>** : returns the maximum element of this stream according to the provided less function. less fuction: a > b
- **<big>Max</big>** : returns the maximum element of this stream according to the provided less function. less function: a > b
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Max)]
[[play](https://go.dev/play/p/fm-1KOPtGzn)]
- **<big>Min</big>** : returns the minimum element of this stream according to the provided less function. less fuction: a < b
- **<big>Min</big>** : returns the minimum element of this stream according to the provided less function. less function: a < b
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/stream.md#Min)]
[[play](https://go.dev/play/p/vZfIDgGNRe_0)]
- **<big>AllMatch</big>** : returns whether all elements of this stream match the provided predicate.
@@ -1433,29 +1511,31 @@ import "github.com/duke-git/lancet/v2/structs"
#### Function list:
- **<big>New</big>** : creates a `Struct` instance.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/struct.md#New)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#New)]
- **<big>ToMap</big>** : converts a valid struct to a map.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/struct.md#ToMap)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#ToMap)]
- **<big>Fields</big>** : get all fields of a given struct, that the fields are abstract struct field.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/struct.md#Fields)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Fields)]
- **<big>IsStruct</big>** : check if the struct is valid.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/struct.md#IsStruct)]
- **<big>Tag</big>** : get a `Tag` of the `Field`, `Tag` is a abstract struct field tag
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/field.md#Tag)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsStruct)]
- **<big>Tag</big>** : get a `Tag` of the `Field`, `Tag` is a abstract struct field tag.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Tag)]
- **<big>Name</big>** : get the field name.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/field.md#Name)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Name)]
- **<big>Value</big>** : get the `Field` underlying value.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/field.md#Value)]
- **<big>Kind</big>** : get the field's kind
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/field.md#Kind)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Value)]
- **<big>Kind</big>** : get the field's kind.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#Kind)]
- **<big>IsEmbedded</big>** : check if the field is an embedded field.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/field.md#IsEmbedded)]
- **<big>IsExported</big>** : check if the field is exporte
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/field.md#IsExported)]
- **<big>IsZero</big>** : check if the field is zero value
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/field.md#IsZero)]
- **<big>IsSlice</big>** : check if the field is a slice
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/structs/field.md#IsSlice)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsEmbedded)]
- **<big>IsExported</big>** : check if the field is exported.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsExported)]
- **<big>IsZero</big>** : check if the field is zero value.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsZero)]
- **<big>IsSlice</big>** : check if the field is a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsSlice)]
- **<big>IsTargetType</big>** : check if the field is target type.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/struct.md#IsTargetType)]
<h3 id="strutil"> 21. Strutil package contains some functions to manipulate string. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1553,6 +1633,7 @@ import "github.com/duke-git/lancet/v2/strutil"
[[play](https://go.dev/play/p/6zXRH_c0Qd3)]
- **<big>IsNotBlank</big>** : checks if a string is not whitespace or not empty.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#IsNotBlank)]
[[play](https://go.dev/play/p/e_oJW0RAquA)]
- **<big>HasPrefixAny</big>** : checks if a string starts with any of an array of specified strings.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/strutil.md#HasPrefixAny)]
[[play](https://go.dev/play/p/8UUTl2C5slo)]
@@ -1577,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. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1623,7 +1707,7 @@ import "github.com/duke-git/lancet/v2/tuple"
#### Function list:
- **<big>Tuple2</big>** : represents a 2 elemnets tuple.
- **<big>Tuple2</big>** : represents a 2 elements tuple.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple2)]
[[play](https://go.dev/play/p/3sHVqBQpLYN)]
- **<big>Tuple2_Unbox</big>** : returns values in Tuple2.
@@ -1635,7 +1719,7 @@ import "github.com/duke-git/lancet/v2/tuple"
- **<big>Unzip2</big>** : create a group of slice from a slice of Tuple2.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip2)]
[[play](https://go.dev/play/p/KBecr60feXb)]
- **<big>Tuple3</big>** : represents a 3 elemnets tuple.
- **<big>Tuple3</big>** : represents a 3 elements tuple.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple3)]
[[play](https://go.dev/play/p/FtH2sdCLlCf)]
- **<big>Tuple3_Unbox</big>** : returns values in Tuple3.
@@ -1647,7 +1731,7 @@ import "github.com/duke-git/lancet/v2/tuple"
- **<big>Unzip3</big>** : create a group of slice from a slice of Tuple3.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip3)]
[[play](https://go.dev/play/p/bba4cpAa7KO)]
- **<big>Tuple4</big>** : represents a 4 elemnets tuple.
- **<big>Tuple4</big>** : represents a 4 elements tuple.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple4)]
[[play](https://go.dev/play/p/D2EqDz096tk)]
- **<big>Tuple4_Unbox</big>** : returns values in Tuple4.
@@ -1659,7 +1743,7 @@ import "github.com/duke-git/lancet/v2/tuple"
- **<big>Unzip4</big>** : create a group of slice from a slice of Tuple4.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip4)]
[[play](https://go.dev/play/p/rb8z4gyYSRN)]
- **<big>Tuple5</big>** : represents a 5 elemnets tuple.
- **<big>Tuple5</big>** : represents a 5 elements tuple.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple5)]
[[play](https://go.dev/play/p/2WndmVxPg-r)]
- **<big>Tuple5_Unbox</big>** : returns values in Tuple4.
@@ -1671,7 +1755,7 @@ import "github.com/duke-git/lancet/v2/tuple"
- **<big>Unzip5</big>** : create a group of slice from a slice of Tuple5.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip5)]
[[play](https://go.dev/play/p/gyl6vKfhqPb)]
- **<big>Tuple6</big>** : represents a 6 elemnets tuple.
- **<big>Tuple6</big>** : represents a 6 elements tuple.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple6)]
[[play](https://go.dev/play/p/VjqcCwEJZbs)]
- **<big>Tuple6_Unbox</big>** : returns values in Tuple6.
@@ -1683,7 +1767,7 @@ import "github.com/duke-git/lancet/v2/tuple"
- **<big>Unzip6</big>** : create a group of slice from a slice of Tuple6.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip6)]
[[play](https://go.dev/play/p/l41XFqCyh5E)]
- **<big>Tuple7</big>** : represents a 7 elemnets tuple.
- **<big>Tuple7</big>** : represents a 7 elements tuple.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple7)]
[[play](https://go.dev/play/p/dzAgv_Ezub9)]
- **<big>Tuple7_Unbox</big>** : returns values in Tuple7.
@@ -1695,7 +1779,7 @@ import "github.com/duke-git/lancet/v2/tuple"
- **<big>Unzip7</big>** : create a group of slice from a slice of Tuple7.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip7)]
[[play](https://go.dev/play/p/hws_P1Fr2j3)]
- **<big>Tuple8</big>** : represents a 8 elemnets tuple.
- **<big>Tuple8</big>** : represents a 8 elements tuple.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple8)]
[[play](https://go.dev/play/p/YA9S0rz3dRz)]
- **<big>Tuple8_Unbox</big>** : returns values in Tuple8.
@@ -1707,7 +1791,7 @@ import "github.com/duke-git/lancet/v2/tuple"
- **<big>Unzip8</big>** : create a group of slice from a slice of Tuple8.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip8)]
[[play](https://go.dev/play/p/1SndOwGsZB4)]
- **<big>Tuple9</big>** : represents a 9 elemnets tuple.
- **<big>Tuple9</big>** : represents a 9 elements tuple.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple9)]
[[play](https://go.dev/play/p/yS2NGGtZpQr)]
- **<big>Tuple9_Unbox</big>** : returns values in Tuple9.
@@ -1719,7 +1803,7 @@ import "github.com/duke-git/lancet/v2/tuple"
- **<big>Unzip9</big>** : create a group of slice from a slice of Tuple9.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Unzip9)]
[[play](https://go.dev/play/p/91-BU_KURSA)]
- **<big>Tuple10</big>** : represents a 10 elemnets tuple.
- **<big>Tuple10</big>** : represents a 10 elements tuple.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/tuple.md#Tuple10)]
[[play](https://go.dev/play/p/799qqZg0hUv)]
- **<big>Tuple10_Unbox</big>** : returns values in Tuple10.
@@ -1851,19 +1935,19 @@ import "github.com/duke-git/lancet/v2/validator"
- **<big>IsJWT</big>** : check if a give string is a valid JSON Web Token (JWT).
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsJWT)]
[[play](https://go.dev/play/p/R6Op7heJbKI)]
- **<big>IsVisa</big>** : check if a give string is a valid visa card nubmer or not.
- **<big>IsVisa</big>** : check if a give string is a valid visa card number or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsVisa)]
[[play](https://go.dev/play/p/SdS2keOyJsl)]
- **<big>IsMasterCard</big>** : check if a give string is a valid master card nubmer or not.
- **<big>IsMasterCard</big>** : check if a give string is a valid master card number or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsMasterCard)]
[[play](https://go.dev/play/p/CwWBFRrG27b)]
- **<big>IsAmericanExpress</big>** : check if a give string is a valid american expression card nubmer or not.
- **<big>IsAmericanExpress</big>** : check if a give string is a valid american expression card number or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsAmericanExpress)]
[[play](https://go.dev/play/p/HIDFpcOdpkd)]
- **<big>IsUnionPay</big>** : check if a give string is a valid union pay nubmer or not.
- **<big>IsUnionPay</big>** : check if a give string is a valid union pay number or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsUnionPay)]
[[play](https://go.dev/play/p/CUHPEwEITDf)]
- **<big>IsChinaUnionPay</big>** : check if a give string is a valid china union pay nubmer or not.
- **<big>IsChinaUnionPay</big>** : check if a give string is a valid china union pay number or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/validator.md#IsChinaUnionPay)]
[[play](https://go.dev/play/p/yafpdxLiymu)]
@@ -1919,22 +2003,6 @@ import "github.com/duke-git/lancet/v2/xerror"
#### [Contributing Guide](./CONTRIBUTING.en-US.md)
## Sponsor
Hello, I am a software developer and have been engaged in development work for 13 years. Love open source software. And be willing to put in the energy for it. I am the creator of project lancet. Since Lancet was released as open source two years ago, it has been used by more than 1,000 internal and external projects. lancet will always be free for all users. Your support is a powerful encouragement for me to continue my struggle. Thanks! You can use WeChat to scans the following QR code or clicks the following sponsor button to initiate sponsorship.
<div style="position: relative;display: inline-block;">
<img src="./docs/public/wechat_pay.png" width="260" height="260"/>
<a href="https://en.liberapay.com/Duke_Du/donate" target="\_blank"><img src="./docs/public/sponsor_btn.png" width="220" height="60"/></a>
</div>
<br/>
<br/>
*Donated funds will be used to maintain [lancet](https://www.golancet.cn/en/) website and pay for cloud server costs. Or just buy me a cup of ☕️ when I'm sleepy writing code.*
## Contributors
Thank you to all the people who contributed to lancet!

View File

@@ -4,7 +4,7 @@
<br/>
![Go version](https://img.shields.io/badge/go-%3E%3Dv1.18-9cf)
[![Release](https://img.shields.io/badge/release-2.2.7-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.0-green.svg)](https://github.com/duke-git/lancet/releases)
[![GoDoc](https://godoc.org/github.com/duke-git/lancet/v2?status.svg)](https://pkg.go.dev/github.com/duke-git/lancet/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/duke-git/lancet/v2)](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
[![test](https://github.com/duke-git/lancet/actions/workflows/codecov.yml/badge.svg?branch=main&event=push)](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 包实现一些基本查找和排序算法。 &nbsp; &nbsp; &nbsp; &nbsp;<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 加密包支持数据加密和解密,获取 md5hash 值。支持 base64, md5, hmac, aes, des, rsa。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -453,10 +463,14 @@ import "github.com/duke-git/lancet/v2/cryptor"
[[play](https://go.dev/play/p/uef0q1fz53I)]
- **<big>GenerateRsaKeyPair</big>** : 创建rsa公钥私钥和key。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#GenerateRsaKeyPair)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
- **<big>RsaEncryptOAEP</big>** : rsa OAEP加密。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaEncryptOAEP)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
- **<big>RsaDecryptOAEP</big>** : rsa OAEP解密。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/cryptor.md#RsaDecryptOAEP)]
[[play](https://go.dev/play/p/sSVmkfENKMz)]
<h3 id="datetime"> 7. datetime 日期时间处理包,格式化日期,比较日期。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -568,9 +582,6 @@ import "github.com/duke-git/lancet/v2/datetime"
- **<big>IsLeapYear</big>** :验证是否是闰年。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#IsLeapYear)]
[[play](https://go.dev/play/p/xS1eS2ejGew)]
- **<big>IsLeapYear</big>** : check if param `year` is leap year or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#IsLeapYear)]
[[play](https://go.dev/play/p/xS1eS2ejGew)]
- **<big>BetweenSeconds</big>** : 返回两个时间的间隔秒数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BetweenSeconds)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/datetime.md#BetweenSeconds)]
@@ -704,9 +715,11 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>ReadCsvFile</big>** : 读取 csv 文件内容到切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadCsvFile)]
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
- **<big>WriteCsvFile</big>** : 向 csv 文件写入内容
- **<big>WriteCsvFile</big>** : 向csv文件写入切片数据
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteCsvFile)]
[[play](https://go.dev/play/p/dAXm58Q5U1o)]
- **<big>WriteMapsToCsv</big>** : 将map切片写入csv文件中。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteMapsToCsv)]
[[play](https://go.dev/play/p/umAIomZFV1c)]
- **<big>WriteBytesToFile</big>** : 将 bytes 写入文件。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteBytesToFile)]
[[play](https://go.dev/play/p/s7QlDxMj3P8)]
@@ -715,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 格式化器包含一些数据格式化处理方法。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -778,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)]
@@ -925,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)]
@@ -964,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 请求。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1097,6 +1142,16 @@ import "github.com/duke-git/lancet/v2/random"
- **<big>RandUniqueIntSlice</big>** : 生成一个不重复的长度为 n 的随机 int 切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandUniqueIntSlice)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
- **<big>RandSymbolChar</big>** : 生成给定长度的随机符号字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandSymbolChar)]
[[play](https://go.dev/play/p/Im6ZJxAykOm)]
- **<big>RandFloat</big>** : 生成随机float64数字可以指定范围和精度。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloat)]
[[play](https://go.dev/play/p/zbD_tuobJtr)]
- **<big>RandFloats</big>** : 生成随机float64数字切片可以指定长度范围和精度.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/random.md#RandFloats)]
[[play](https://go.dev/play/p/uBkRSOz73Ec)]
<h3 id="retry"> 17. retry 重试执行函数直到函数运行成功或被 context cancel。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1121,8 +1176,18 @@ 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 包含操作切片的方法集合。&nbsp; &nbsp; &nbsp; &nbsp; [回到目录](#index)
<h3 id="slice"> 18. slice 包含操作切片的方法集合。&nbsp; &nbsp; &nbsp; &nbsp; <a href="#index">回到目录</a></h3>
```go
import "github.com/duke-git/lancet/v2/slice"
@@ -1166,9 +1231,12 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>DifferenceWith</big>** : 接受比较器函数,该比较器被调用以将切片的元素与值进行比较。 结果值的顺序和引用由第一个切片确定。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DifferenceWith)]
[[play](https://go.dev/play/p/v2U2deugKuV)]
- **<big>DeleteAt</big>** : 删除切片中指定开始索引到结束索引的元素。
- **<big>DeleteAt</big>** : 删除切片中指定索引到的元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteAt)]
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
[[play](https://go.dev/play/p/800B1dPBYyd)]
- **<big>DeleteRange</big>** : 删除切片中指定开始索引到结束索引的元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteRange)]
[[play](https://go.dev/play/p/945HwiNrnle)]
- **<big>Drop</big>** : 从切片头部删除 n 个元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Drop)]
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
@@ -1331,7 +1399,6 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Without</big>** : 创建一个不包括所有给定值的切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Without)]
[[play](https://go.dev/play/p/bwhEXEypThg)]
- **<big>KeyBy</big>** :将切片每个元素调用函数后转为 map。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#KeyBy)]
[[play](https://go.dev/play/p/uXod2LWD1Kg)]
- **<big>Join</big>** : 用指定的分隔符链接切片元素。
@@ -1340,6 +1407,13 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Partition</big>** : 根据给定的predicate判断函数分组切片元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Partition)]
[[play](https://go.dev/play/p/lkQ3Ri2NQhV)]
- **<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)]
<h3 id="stream"> 19. stream 流,该包仅验证简单的 stream 实现,功能有限。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1437,31 +1511,33 @@ import "github.com/duke-git/lancet/v2/structs"
#### 函数列表:
- **<big>New</big>** : `Struct`结构体的构造函数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/struct.md#New)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#New)]
- **<big>ToMap</big>** : 将一个合法的 struct 对象转换为 map[string]any。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/struct.md#ToMap)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#ToMap)]
- **<big>Fields</big>** : 获取一个 struct 对象的属性列表。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/struct.md#Fields)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Fields)]
- **<big>Field</big>** : 根据属性名获取一个 struct 对象的属性。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/struct.md#Fields)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Fields)]
- **<big>IsStruct</big>** : 判断是否为一个合法的 struct 对象。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/struct.md#IsStruct)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsStruct)]
- **<big>Tag</big>** : 获取`Field``Tag`,默认的 tag key 是 json。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/field.md#Tag)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Tag)]
- **<big>Name</big>** : 获取属性名。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/field.md#Name)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Name)]
- **<big>Value</big>** : 获取`Field`属性的值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/field.md#Value)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Value)]
- **<big>Kind</big>** : 获取属性 Kind。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/field.md#Kind)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#Kind)]
- **<big>IsEmbedded</big>** : 判断属性是否为嵌入。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/field.md#IsEmbedded)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsEmbedded)]
- **<big>IsExported</big>** : 判断属性是否导出。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/field.md#IsExported)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsExported)]
- **<big>IsZero</big>** : 判断属性是否为零值。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/field.md#IsZero)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsZero)]
- **<big>IsSlice</big>** : 判断属性是否是切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/structs/field.md#IsSlice)]
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsSlice)]
- **<big>IsTargetType</big>** : 判断属性是否是目标类型。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/struct.md#IsTargetType)]
<h3 id="strutil"> 21. strutil 包含字符串处理的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1560,6 +1636,7 @@ import "github.com/duke-git/lancet/v2/strutil"
[[play](https://go.dev/play/p/6zXRH_c0Qd3)]
- **<big>IsNotBlank</big>** : 检查字符串是否不为空。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#IsNotBlank)]
[[play](https://go.dev/play/p/e_oJW0RAquA)]
- **<big>HasPrefixAny</big>** : 检查字符串是否以指定字符串数组中的任何一个开头。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HasPrefixAny)]
[[play](https://go.dev/play/p/8UUTl2C5slo)]
@@ -1584,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 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1925,23 +2006,9 @@ import "github.com/duke-git/lancet/v2/xerror"
#### [贡献代码指南](./CONTRIBUTING.zh-CN.md)
## 赞助
您好,我是一名软件开发者,从事开发工作 13 年。热爱软件开源。并愿意为此付出精力。是开源项目 lancet 的作者。Lancet 自两年前开源发布以来,已有超过 1000 个内外部项目使用。lancet 一直会对所有用户免费。您的支持是对我继续奋斗的有力鼓励。谢谢! 微信扫描以下二维码或点击以下赞助按钮发起赞助。
<div style="position: relative;display: inline-block;">
<img src="./docs/public/wechat_pay.png" width="260" height="260"/>
<a href="https://liberapay.com/Duke_Du/donate" target="\_blank"><img src="./docs/public/sponsor_btn.png" width="220" height="60"/></a>
</div>
<br/>
<br/>
_捐赠的资金将用于后续[lancet](https://www.golancet.cn/)官网的维护和云服务器的费用支付。或者当我写代码困倦时,给我买杯 ☕️。_
## 贡献者
感谢所有为 lancet 贡献过代码的人!
感谢所有为lancet贡献过代码的人
<a href="https://github.com/duke-git/lancet/graphs/contributors">
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />

View File

@@ -4,7 +4,7 @@
// Package algorithm contain some basic algorithm functions. eg. sort, search, list, linklist, stack, queue, tree, graph.
package algorithm
import "github.com/duke-git/lancet/v2/lancetconstraints"
import "github.com/duke-git/lancet/v2/constraints"
// Search algorithms see https://github.com/TheAlgorithms/Go/tree/master/search
@@ -23,7 +23,7 @@ func LinearSearch[T any](slice []T, target T, equal func(a, b T) bool) int {
// BinarySearch return the index of target within a sorted slice, use binary search (recursive call itself).
// If not found return -1.
// Play: https://go.dev/play/p/t6MeGiUSN47
func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int {
func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int {
if highIndex < lowIndex || len(sortedSlice) == 0 {
return -1
}
@@ -44,7 +44,7 @@ func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, com
// BinaryIterativeSearch return the index of target within a sorted slice, use binary search (no recursive).
// If not found return -1.
// Play: https://go.dev/play/p/Anozfr8ZLH3
func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int {
func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int {
startIndex := lowIndex
endIndex := highIndex

View File

@@ -3,24 +3,29 @@
package algorithm
import "github.com/duke-git/lancet/v2/lancetconstraints"
import "github.com/duke-git/lancet/v2/constraints"
// BubbleSort applys the bubble sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/GNdv7Jg2Taj
func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator) {
func BubbleSort[T any](slice []T, comparator constraints.Comparator) {
for i := 0; i < len(slice); i++ {
breakTag := false
for j := 0; j < len(slice)-1-i; j++ {
isCurrGreatThanNext := comparator.Compare(slice[j], slice[j+1]) == 1
if isCurrGreatThanNext {
swap(slice, j, j+1)
breakTag = true
}
}
if !breakTag {
break
}
}
}
// InsertionSort applys the insertion sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/G5LJiWgJJW6
func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
func InsertionSort[T any](slice []T, comparator constraints.Comparator) {
for i := 0; i < len(slice); i++ {
for j := i; j > 0; j-- {
isPreLessThanCurrent := comparator.Compare(slice[j], slice[j-1]) == -1
@@ -35,7 +40,7 @@ func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
// SelectionSort applys the selection sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/oXovbkekayS
func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
func SelectionSort[T any](slice []T, comparator constraints.Comparator) {
for i := 0; i < len(slice); i++ {
min := i
for j := i + 1; j < len(slice); j++ {
@@ -49,7 +54,7 @@ func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator) {
// ShellSort applys the shell sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/3ibkszpJEu3
func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) {
func ShellSort[T any](slice []T, comparator constraints.Comparator) {
size := len(slice)
gap := 1
@@ -69,11 +74,11 @@ func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator) {
// QuickSort quick sorting for slice, lowIndex is 0 and highIndex is len(slice)-1.
// Play: https://go.dev/play/p/7Y7c1Elk3ax
func QuickSort[T any](slice []T, comparator lancetconstraints.Comparator) {
func QuickSort[T any](slice []T, comparator constraints.Comparator) {
quickSort(slice, 0, len(slice)-1, comparator)
}
func quickSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) {
func quickSort[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) {
if lowIndex < highIndex {
p := partition(slice, lowIndex, highIndex, comparator)
quickSort(slice, lowIndex, p-1, comparator)
@@ -82,7 +87,7 @@ func quickSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconst
}
// partition split slice into two parts
func partition[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int {
func partition[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) int {
p := slice[highIndex]
i := lowIndex
for j := lowIndex; j < highIndex; j++ {
@@ -99,7 +104,7 @@ func partition[T any](slice []T, lowIndex, highIndex int, comparator lancetconst
// HeapSort applys the heap sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/u6Iwa1VZS_f
func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator) {
func HeapSort[T any](slice []T, comparator constraints.Comparator) {
size := len(slice)
for i := size/2 - 1; i >= 0; i-- {
@@ -111,7 +116,7 @@ func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator) {
}
}
func sift[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) {
func sift[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) {
i := lowIndex
j := 2*i + 1
@@ -133,11 +138,11 @@ func sift[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraint
// MergeSort applys the merge sort algorithm to sort the collection, will change the original collection data.
// Play: https://go.dev/play/p/ydinn9YzUJn
func MergeSort[T any](slice []T, comparator lancetconstraints.Comparator) {
func MergeSort[T any](slice []T, comparator constraints.Comparator) {
mergeSort(slice, 0, len(slice)-1, comparator)
}
func mergeSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) {
func mergeSort[T any](slice []T, lowIndex, highIndex int, comparator constraints.Comparator) {
if lowIndex < highIndex {
mid := (lowIndex + highIndex) / 2
mergeSort(slice, lowIndex, mid, comparator)
@@ -146,7 +151,7 @@ func mergeSort[T any](slice []T, lowIndex, highIndex int, comparator lancetconst
}
}
func merge[T any](slice []T, lowIndex, midIndex, highIndex int, comparator lancetconstraints.Comparator) {
func merge[T any](slice []T, lowIndex, midIndex, highIndex int, comparator constraints.Comparator) {
i := lowIndex
j := midIndex + 1
temp := []T{}
@@ -175,7 +180,7 @@ func merge[T any](slice []T, lowIndex, midIndex, highIndex int, comparator lance
// CountSort applys the count sort algorithm to sort the collection, don't change the original collection data.
// Play: https://go.dev/play/p/tB-Umgm0DrP
func CountSort[T any](slice []T, comparator lancetconstraints.Comparator) []T {
func CountSort[T any](slice []T, comparator constraints.Comparator) []T {
size := len(slice)
out := make([]T, size)

View File

@@ -16,7 +16,7 @@ type people struct {
// PeopleAageComparator sort people slice by age field
type peopleAgeComparator struct{}
// Compare implements github.com/duke-git/lancet/v2/lancetconstraints/constraints.go/Comparator
// Compare implements github.com/duke-git/lancet/v2/constraints/constraints.go/Comparator
func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int {
p1, _ := v1.(people)
p2, _ := v2.(people)

View File

@@ -70,7 +70,7 @@ func compareRefValue(operator string, leftObj, rightObj any, kind reflect.Kind)
switch operator {
case equal:
if bytes.Compare(bytesObj1, bytesObj2) == 0 {
if bytes.Equal(bytesObj1, bytesObj2) {
return true
}
case lessThan:

View File

@@ -1,6 +1,7 @@
package compare
import (
"encoding/json"
"testing"
"time"
@@ -11,6 +12,7 @@ func TestEqual(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestEqual")
// basic type
assert.Equal(true, Equal(1, 1))
assert.Equal(true, Equal(int64(1), int64(1)))
assert.Equal(true, Equal("a", "a"))
@@ -25,6 +27,7 @@ func TestEqual(t *testing.T) {
assert.Equal(false, Equal([]int{1, 2}, []int{1, 2, 3}))
assert.Equal(false, Equal(map[int]string{1: "a", 2: "b"}, map[int]string{1: "a"}))
// time
time1 := time.Now()
time2 := time1.Add(time.Second)
time3 := time1.Add(time.Second)
@@ -32,6 +35,7 @@ func TestEqual(t *testing.T) {
assert.Equal(false, Equal(time1, time2))
assert.Equal(true, Equal(time2, time3))
// struct
st1 := struct {
A string
B string
@@ -58,6 +62,19 @@ func TestEqual(t *testing.T) {
assert.Equal(true, Equal(st1, st2))
assert.Equal(false, Equal(st1, st3))
//byte slice
bs1 := []byte("hello")
bs2 := []byte("hello")
assert.Equal(true, Equal(bs1, bs2))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`123`), &jsonNumber2)
assert.Equal(true, Equal(jsonNumber1, jsonNumber2))
}
func TestEqualValue(t *testing.T) {
@@ -69,6 +86,13 @@ func TestEqualValue(t *testing.T) {
assert.Equal(true, EqualValue(1, "1"))
assert.Equal(false, EqualValue(1, "2"))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`123`), &jsonNumber2)
assert.Equal(true, EqualValue(jsonNumber1, 123))
}
func TestLessThan(t *testing.T) {
@@ -85,6 +109,17 @@ func TestLessThan(t *testing.T) {
assert.Equal(false, LessThan(1, 1))
assert.Equal(false, LessThan(1, int64(1)))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, LessThan(bs1, bs2))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, LessThan(jsonNumber1, jsonNumber2))
}
func TestGreaterThan(t *testing.T) {
@@ -102,6 +137,17 @@ func TestGreaterThan(t *testing.T) {
assert.Equal(false, GreaterThan(1, 2))
assert.Equal(false, GreaterThan(int64(2), 1))
assert.Equal(false, GreaterThan("b", "c"))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, GreaterThan(bs2, bs1))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, GreaterThan(jsonNumber2, jsonNumber1))
}
func TestLessOrEqual(t *testing.T) {
@@ -119,6 +165,17 @@ func TestLessOrEqual(t *testing.T) {
assert.Equal(false, LessOrEqual(2, 1))
assert.Equal(false, LessOrEqual(1, int64(2)))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, LessOrEqual(bs1, bs2))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, LessOrEqual(jsonNumber1, jsonNumber2))
}
func TestGreaterOrEqual(t *testing.T) {
@@ -137,6 +194,17 @@ func TestGreaterOrEqual(t *testing.T) {
assert.Equal(false, GreaterOrEqual(1, 2))
assert.Equal(false, GreaterOrEqual(int64(2), 1))
assert.Equal(false, GreaterOrEqual("b", "c"))
bs1 := []byte("hello1")
bs2 := []byte("hello2")
assert.Equal(true, GreaterOrEqual(bs2, bs1))
// json.Number
var jsonNumber1, jsonNumber2 json.Number
json.Unmarshal([]byte(`123`), &jsonNumber1)
json.Unmarshal([]byte(`124`), &jsonNumber2)
assert.Equal(true, GreaterOrEqual(jsonNumber2, jsonNumber1))
}
func TestInDelta(t *testing.T) {

View File

@@ -1,8 +1,8 @@
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
// Use of this source code is governed by MIT license
// Package lancetconstraints contain some comstomer constraints.
package lancetconstraints
// Package constraints contain some custom interface.
package constraints
// Comparator is for comparing two values
type Comparator interface {

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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)
@@ -508,14 +578,14 @@ func RsaDecrypt(data []byte, privateKeyFileName string) []byte {
}
// GenerateRsaKeyPair create rsa private and public key.
// Play: todo
// Play: https://go.dev/play/p/sSVmkfENKMz
func GenerateRsaKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey) {
privateKey, _ := rsa.GenerateKey(rand.Reader, keySize)
return privateKey, &privateKey.PublicKey
}
// RsaEncryptOAEP encrypts the given data with RSA-OAEP.
// Play: todo
// Play: https://go.dev/play/p/sSVmkfENKMz
func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error) {
encryptedBytes, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, &key, data, label)
if err != nil {
@@ -526,7 +596,7 @@ func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error
}
// RsaDecryptOAEP decrypts the data with RSA-OAEP.
// Play: todo
// Play: https://go.dev/play/p/sSVmkfENKMz
func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte, error) {
decryptedBytes, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, &key, ciphertext, label)
if err != nil {

View File

@@ -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)))

View File

@@ -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())
}

View File

@@ -7,18 +7,18 @@ package datastructure
import (
"fmt"
"github.com/duke-git/lancet/v2/lancetconstraints"
"github.com/duke-git/lancet/v2/constraints"
)
// MaxHeap implements a binary max heap
// type T should implements Compare function in lancetconstraints.Comparator interface.
// type T should implements Compare function in constraints.Comparator interface.
type MaxHeap[T any] struct {
data []T
comparator lancetconstraints.Comparator
comparator constraints.Comparator
}
// NewMaxHeap returns a MaxHeap instance with the given comparator.
func NewMaxHeap[T any](comparator lancetconstraints.Comparator) *MaxHeap[T] {
func NewMaxHeap[T any](comparator constraints.Comparator) *MaxHeap[T] {
return &MaxHeap[T]{
data: make([]T, 0),
comparator: comparator,
@@ -26,7 +26,7 @@ func NewMaxHeap[T any](comparator lancetconstraints.Comparator) *MaxHeap[T] {
}
// BuildMaxHeap builds a MaxHeap instance with data and given comparator.
func BuildMaxHeap[T any](data []T, comparator lancetconstraints.Comparator) *MaxHeap[T] {
func BuildMaxHeap[T any](data []T, comparator constraints.Comparator) *MaxHeap[T] {
heap := &MaxHeap[T]{
data: make([]T, 0, len(data)),
comparator: comparator,

View File

@@ -90,6 +90,35 @@ func lastIndexOf[T any](o T, e []T, start int, end int) int {
return -1
}
// LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying the
// functional predicate f(T) bool
// if not found return -1.
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int {
index := -1
data := l.getList()
for i := len(data) - 1; i >= 0; i-- {
if f(data[i]) {
index = i
break
}
}
return index
}
// IndexOfFunc returns the first index satisfying the functional predicate f(v) bool
// if not found return -1.
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int {
index := -1
data := l.getList()
for i, v := range data {
if f(v) {
index = i
break
}
}
return index
}
// get returns the element at the specified position in this list.
func get[T any](o []T, index int) *T {
return &o[index]

View File

@@ -1,8 +1,9 @@
package datastructure
import (
"github.com/duke-git/lancet/v2/internal"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestCopyOnWriteList_ValueOf(t *testing.T) {
@@ -233,3 +234,35 @@ func TestCopyOnWriteList_SubList(t *testing.T) {
subList = list.SubList(11, 1)
assert.Equal([]int{}, subList)
}
func TestCopyOnWriteListIndexOfFunc(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestIndexOfFunc")
list := NewCopyOnWriteList([]int{1, 2, 3})
i := list.IndexOfFunc(func(a int) bool { return a == 1 })
assert.Equal(0, i)
i = list.IndexOfFunc(func(a int) bool { return a == 4 })
assert.Equal(-1, i)
}
func TestNewCopyOnWriteListLastIndexOfFunc(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestLastIndexOfFunc")
list := NewCopyOnWriteList([]int{1, 2, 3, 3, 3, 3, 4, 5, 6, 9})
i := list.LastIndexOfFunc(func(a int) bool { return a == 3 })
assert.Equal(5, i)
i = list.LastIndexOfFunc(func(a int) bool { return a == 10 })
assert.Equal(-1, i)
i = list.LastIndexOfFunc(func(a int) bool { return a == 4 })
assert.Equal(6, i)
i = list.LastIndexOfFunc(func(a int) bool { return a == 1 })
assert.Equal(0, i)
}

View File

@@ -0,0 +1,108 @@
package optional
import (
"sync"
)
// Optional is a type that may or may not contain a non-nil value.
type Optional[T any] struct {
value *T
mu *sync.RWMutex
}
// Default returns an default Optional instance.
func Default[T any]() Optional[T] {
return Optional[T]{mu: &sync.RWMutex{}}
}
// Of returns an Optional with a non-nil value.
func Of[T any](value T) Optional[T] {
return Optional[T]{value: &value, mu: &sync.RWMutex{}}
}
// FromNillable returns an Optional for a given value, which may be nil.
func FromNillable[T any](value *T) Optional[T] {
if value == nil {
return Default[T]()
}
return Optional[T]{value: value, mu: &sync.RWMutex{}}
}
// IsNotNil checks if there is a value present.
func (o Optional[T]) IsNotNil() bool {
o.mu.RLock()
defer o.mu.RUnlock()
return o.value != nil
}
// IsNil checks if the Optional is nil.
func (o Optional[T]) IsNil() bool {
return !o.IsNotNil()
}
// IfNotNil performs the given action with the value if a value is not nil.
func (o Optional[T]) IfNotNil(action func(value T)) {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value != nil {
action(*o.value)
}
}
// IfNotNilOrElse performs the action with the value if present, otherwise performs the fallback action.
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func()) {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value != nil {
action(*o.value)
} else {
fallbackAction()
}
}
// Unwarp returns the value if not nil, otherwise panics.
func (o Optional[T]) Unwarp() T {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value == nil {
panic("Optional.Get: no value present")
}
return *o.value
}
// OrElse returns the value if is not nil, otherwise returns other.
func (o Optional[T]) OrElse(other T) T {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value != nil {
return *o.value
}
return other
}
// OrElseGet returns the value if is not nil, otherwise invokes action and returns the result.
func (o Optional[T]) OrElseGet(action func() T) T {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value != nil {
return *o.value
}
return action()
}
// OrElseTrigger returns the value if present, otherwise returns an error.
func (o Optional[T]) OrElseTrigger(errorHandler func() error) (T, error) {
o.mu.RLock()
defer o.mu.RUnlock()
if o.value == nil {
return *new(T), errorHandler()
}
return *o.value, nil
}

View File

@@ -0,0 +1,151 @@
package optional
import (
"errors"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestDefault(t *testing.T) {
assert := internal.NewAssert(t, "TestEmpty")
opt := Default[int]()
assert.ShouldBeTrue(opt.IsNil())
}
func TestOf(t *testing.T) {
assert := internal.NewAssert(t, "TestOf")
value := 42
opt := Of(value)
assert.ShouldBeTrue(opt.IsNotNil())
assert.Equal(opt.Unwarp(), value)
}
func TestFromNillable(t *testing.T) {
assert := internal.NewAssert(t, "TestOfNullable")
var value *int = nil
opt := FromNillable(value)
assert.ShouldBeFalse(opt.IsNotNil())
value = new(int)
*value = 42
opt = FromNillable(value)
assert.ShouldBeTrue(opt.IsNotNil())
}
func TestOrElse(t *testing.T) {
assert := internal.NewAssert(t, "TestOrElse")
optDefault := Default[int]()
defaultValue := 100
val := optDefault.OrElse(defaultValue)
assert.Equal(val, defaultValue)
optWithValue := Of(42)
val = optWithValue.OrElse(defaultValue)
assert.Equal(val, 42)
}
func TestOrElseGetHappyPath(t *testing.T) {
assert := internal.NewAssert(t, "TestOrElseGetHappyPath")
optWithValue := Of(42)
action := func() int { return 100 }
val := optWithValue.OrElseGet(action)
assert.Equal(val, 42)
}
func TestOrElseGet(t *testing.T) {
assert := internal.NewAssert(t, "TestOrElseGet")
optDefault := Default[int]()
action := func() int { return 100 }
val := optDefault.OrElseGet(action)
assert.Equal(val, action())
}
func TestOrElseTrigger(t *testing.T) {
assert := internal.NewAssert(t, "OrElseTrigger")
optDefault := Default[int]()
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
assert.Equal(err.Error(), "no value")
optWithValue := Of(42)
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
assert.IsNil(err)
assert.Equal(val, 42)
}
func TestIfNotNil(t *testing.T) {
assert := internal.NewAssert(t, "IfNotNil")
called := false
action := func(value int) { called = true }
optDefault := Default[int]()
optDefault.IfNotNil(action)
assert.ShouldBeFalse(called)
called = false // Reset for next test
optWithValue := Of(42)
optWithValue.IfNotNil(action)
assert.ShouldBeTrue(called)
}
func TestIfNotNilOrElse(t *testing.T) {
assert := internal.NewAssert(t, "TestIfNotNilOrElse")
// Test when value is present
calledWithValue := false
valueAction := func(value int) { calledWithValue = true }
fallbackAction := func() { t.Errorf("Empty action should not be called when value is present") }
optWithValue := Of(42)
optWithValue.IfNotNilOrElse(valueAction, fallbackAction)
assert.ShouldBeTrue(calledWithValue)
// Test when value is not present
calledWithEmpty := false
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
fallbackAction = func() { calledWithEmpty = true }
optDefault := Default[int]()
optDefault.IfNotNilOrElse(valueAction, fallbackAction)
assert.ShouldBeTrue(calledWithEmpty)
}
func TestGetWithPanicStandard(t *testing.T) {
assert := internal.NewAssert(t, "TestGetWithPanicStandard")
// Test when value is present
optWithValue := Of(42)
func() {
defer func() {
r := recover()
assert.IsNil(r)
}()
val := optWithValue.Unwarp()
if val != 42 {
t.Errorf("Expected Unwarp to return 42, got %v", val)
}
}()
// Test when value is not present
optDefault := Default[int]()
func() {
defer func() {
r := recover()
assert.IsNotNil(r)
}()
_ = optDefault.Unwarp()
}()
}

View File

@@ -8,20 +8,20 @@ package datastructure
import (
"errors"
"github.com/duke-git/lancet/v2/lancetconstraints"
"github.com/duke-git/lancet/v2/constraints"
)
// PriorityQueue is a priority queue implemented by binary heap tree
// type T should implements Compare function in lancetconstraints.Comparator interface.
// type T should implements Compare function in constraints.Comparator interface.
type PriorityQueue[T any] struct {
items []T
size int
comparator lancetconstraints.Comparator
comparator constraints.Comparator
}
// NewPriorityQueue return a pointer of PriorityQueue
// param `comparator` is used to compare values in the queue
func NewPriorityQueue[T any](capacity int, comparator lancetconstraints.Comparator) *PriorityQueue[T] {
func NewPriorityQueue[T any](capacity int, comparator constraints.Comparator) *PriorityQueue[T] {
return &PriorityQueue[T]{
items: make([]T, capacity+1),
size: 0,

View File

@@ -4,19 +4,21 @@
// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate.
package datastructure
import "sort"
// Set is a data container, like slice, but element of set is not duplicate.
type Set[T comparable] map[T]struct{}
// NewSet return a instance of set
func NewSet[T comparable](items ...T) Set[T] {
set := make(Set[T])
// New create a instance of set from given values.
func New[T comparable](items ...T) Set[T] {
set := make(Set[T], len(items))
set.Add(items...)
return set
}
// NewSetFromSlice create a set from slice
func NewSetFromSlice[T comparable](items []T) Set[T] {
set := make(Set[T])
// FromSlice create a set from given slice.
func FromSlice[T comparable](items []T) Set[T] {
set := make(Set[T], len(items))
for _, item := range items {
set.Add(item)
}
@@ -77,8 +79,7 @@ func (s Set[T]) ContainAll(other Set[T]) bool {
// Clone return a copy of set
func (s Set[T]) Clone() Set[T] {
set := NewSet[T]()
set.Add(s.Values()...)
set := FromSlice(s.ToSlice())
return set
}
@@ -116,14 +117,11 @@ func (s Set[T]) Size() int {
}
// Values return all values of set
// Deprecated: Values function is deprecated and will be removed in future versions. Please use ToSlice() function instead.
//
// The ToSlice() function provides the same functionality as Values and returns a slice containing all values of the set.
func (s Set[T]) Values() []T {
result := make([]T, 0, len(s))
s.Iterate(func(value T) {
result = append(result, value)
})
return result
return s.ToSlice()
}
// Union creates a new set contain all element of set s and other
@@ -135,7 +133,7 @@ func (s Set[T]) Union(other Set[T]) Set[T] {
// Intersection creates a new set whose element both be contained in set s and other
func (s Set[T]) Intersection(other Set[T]) Set[T] {
set := NewSet[T]()
set := New[T]()
s.Iterate(func(value T) {
if other.Contain(value) {
set.Add(value)
@@ -147,7 +145,7 @@ func (s Set[T]) Intersection(other Set[T]) Set[T] {
// SymmetricDifference creates a new set whose element is in set1 or set2, but not in both sets
func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
set := NewSet[T]()
set := New[T]()
s.Iterate(func(value T) {
if !other.Contain(value) {
set.Add(value)
@@ -163,9 +161,9 @@ func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
return set
}
// Minus creates an set of whose element in origin set but not in compared set
// Minus creates a set of whose element in origin set but not in compared set
func (s Set[T]) Minus(comparedSet Set[T]) Set[T] {
set := NewSet[T]()
set := New[T]()
s.Iterate(func(value T) {
if !comparedSet.Contain(value) {
@@ -197,3 +195,25 @@ func (s Set[T]) Pop() (v T, ok bool) {
return v, false
}
// ToSlice returns a slice containing all values of the set.
func (s Set[T]) ToSlice() []T {
if s.IsEmpty() {
return []T{}
}
result := make([]T, 0, s.Size())
s.Iterate(func(value T) {
result = append(result, value)
})
return result
}
// ToSortedSlice returns a sorted slice containing all values of the set.
func (s Set[T]) ToSortedSlice(less func(v1, v2 T) bool) []T {
result := s.ToSlice()
sort.Slice(result, func(i, j int) bool {
return less(result[i], result[j])
})
return result
}

View File

@@ -1,23 +1,25 @@
package datastructure
import (
"reflect"
"sort"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestSet_NewSetFromSlice(t *testing.T) {
func TestSet_FromSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_NewSetFromSlice")
assert := internal.NewAssert(t, "TestSet_FromSlice")
s1 := NewSetFromSlice([]int{1, 2, 2, 3})
s1 := FromSlice([]int{1, 2, 2, 3})
assert.Equal(3, s1.Size())
assert.Equal(true, s1.Contain(1))
assert.Equal(true, s1.Contain(2))
assert.Equal(true, s1.Contain(3))
s2 := NewSetFromSlice([]int{})
s2 := FromSlice([]int{})
assert.Equal(0, s2.Size())
}
@@ -26,10 +28,10 @@ func TestSet_Add(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Add")
set := NewSet[int]()
set := New[int]()
set.Add(1, 2, 3)
cmpSet := NewSet(1, 2, 3)
cmpSet := New(1, 2, 3)
assert.Equal(true, set.Equal(cmpSet))
}
@@ -39,12 +41,12 @@ func TestSet_AddIfNotExist(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_AddIfNotExist")
set := NewSet[int]()
set := New[int]()
set.Add(1, 2, 3)
assert.Equal(false, set.AddIfNotExist(1))
assert.Equal(true, set.AddIfNotExist(4))
assert.Equal(NewSet(1, 2, 3, 4), set)
assert.Equal(New(1, 2, 3, 4), set)
}
func TestSet_AddIfNotExistBy(t *testing.T) {
@@ -52,7 +54,7 @@ func TestSet_AddIfNotExistBy(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_AddIfNotExistBy")
set := NewSet[int]()
set := New[int]()
set.Add(1, 2)
ok := set.AddIfNotExistBy(3, func(val int) bool {
@@ -75,7 +77,7 @@ func TestSet_Contain(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Contain")
set := NewSet[int]()
set := New[int]()
set.Add(1, 2, 3)
assert.Equal(true, set.Contain(1))
@@ -87,9 +89,9 @@ func TestSet_ContainAll(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_ContainAll")
set1 := NewSet(1, 2, 3)
set2 := NewSet(1, 2)
set3 := NewSet(1, 2, 3, 4)
set1 := New(1, 2, 3)
set2 := New(1, 2)
set3 := New(1, 2, 3, 4)
assert.Equal(true, set1.ContainAll(set2))
assert.Equal(false, set1.ContainAll(set3))
@@ -100,7 +102,7 @@ func TestSet_Clone(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Clone")
set1 := NewSet(1, 2, 3)
set1 := New(1, 2, 3)
set2 := set1.Clone()
assert.Equal(true, set1.Size() == set2.Size())
@@ -112,11 +114,11 @@ func TestSet_Delete(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Delete")
set := NewSet[int]()
set := New[int]()
set.Add(1, 2, 3)
set.Delete(3)
assert.Equal(true, set.Equal(NewSet(1, 2)))
assert.Equal(true, set.Equal(New(1, 2)))
}
func TestSet_Equal(t *testing.T) {
@@ -124,9 +126,9 @@ func TestSet_Equal(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Equal")
set1 := NewSet(1, 2, 3)
set2 := NewSet(1, 2, 3)
set3 := NewSet(1, 2, 3, 4)
set1 := New(1, 2, 3)
set2 := New(1, 2, 3)
set3 := New(1, 2, 3, 4)
assert.Equal(true, set1.Equal(set2))
assert.Equal(false, set1.Equal(set3))
@@ -137,7 +139,7 @@ func TestSet_Iterate(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Iterate")
set := NewSet(1, 2, 3)
set := New(1, 2, 3)
arr := []int{}
set.Iterate(func(value int) {
arr = append(arr, value)
@@ -151,7 +153,7 @@ func TestSet_IsEmpty(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_IsEmpty")
set := NewSet[int]()
set := New[int]()
assert.Equal(true, set.IsEmpty())
}
@@ -160,7 +162,7 @@ func TestSet_Size(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Size")
set := NewSet(1, 2, 3)
set := New(1, 2, 3)
assert.Equal(3, set.Size())
}
@@ -169,7 +171,7 @@ func TestSet_Values(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Values")
set := NewSet(1, 2, 3)
set := New(1, 2, 3)
values := set.Values()
assert.Equal(3, len(values))
@@ -180,12 +182,12 @@ func TestSet_Union(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Union")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
set1 := New(1, 2, 3)
set2 := New(2, 3, 4, 5)
unionSet := set1.Union(set2)
assert.Equal(NewSet(1, 2, 3, 4, 5), unionSet)
assert.Equal(New(1, 2, 3, 4, 5), unionSet)
}
func TestSet_Intersection(t *testing.T) {
@@ -193,11 +195,11 @@ func TestSet_Intersection(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Intersection")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
set1 := New(1, 2, 3)
set2 := New(2, 3, 4, 5)
intersectionSet := set1.Intersection(set2)
assert.Equal(NewSet(2, 3), intersectionSet)
assert.Equal(New(2, 3), intersectionSet)
}
func TestSet_SymmetricDifference(t *testing.T) {
@@ -205,10 +207,10 @@ func TestSet_SymmetricDifference(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_SymmetricDifference")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
set1 := New(1, 2, 3)
set2 := New(2, 3, 4, 5)
assert.Equal(NewSet(1, 4, 5), set1.SymmetricDifference(set2))
assert.Equal(New(1, 4, 5), set1.SymmetricDifference(set2))
}
func TestSet_Minus(t *testing.T) {
@@ -216,16 +218,16 @@ func TestSet_Minus(t *testing.T) {
assert := internal.NewAssert(t, "TestSet_Minus")
set1 := NewSet(1, 2, 3)
set2 := NewSet(2, 3, 4, 5)
set3 := NewSet(2, 3)
set1 := New(1, 2, 3)
set2 := New(2, 3, 4, 5)
set3 := New(2, 3)
assert.Equal(NewSet(1), set1.Minus(set2))
assert.Equal(NewSet(4, 5), set2.Minus(set3))
assert.Equal(New(1), set1.Minus(set2))
assert.Equal(New(4, 5), set2.Minus(set3))
}
func TestEachWithBreak(t *testing.T) {
// s := NewSet(1, 2, 3, 4, 5)
// s := New(1, 2, 3, 4, 5)
// var sum int
@@ -244,7 +246,7 @@ func TestEachWithBreak(t *testing.T) {
// func TestPop(t *testing.T) {
// assert := internal.NewAssert(t, "TestPop")
// s := NewSet[int]()
// s := New[int]()
// val, ok := s.Pop()
// assert.Equal(0, val)
@@ -254,9 +256,71 @@ func TestEachWithBreak(t *testing.T) {
// s.Add(2)
// s.Add(3)
// // s = NewSet(1, 2, 3, 4, 5)
// // s = New(1, 2, 3, 4, 5)
// val, ok = s.Pop()
// assert.Equal(3, val)
// assert.Equal(true, ok)
// }
func TestSet_ToSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_ToSlice")
set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1})
set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0})
set3 := New[string]()
slice1 := set1.ToSlice()
slice2 := set2.ToSlice()
slice3 := set3.ToSlice()
sort.Ints(slice1)
sort.Float64s(slice2)
assert.Equal(5, len(slice1))
assert.Equal(4, len(slice2))
assert.Equal(0, len(slice3))
assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7}))
assert.Equal(true, reflect.DeepEqual(slice2, []float64{-2.65, 0, 1.11, 4.25}))
assert.Equal("[]string", reflect.TypeOf(slice3).String())
}
func TestSet_ToSortedSlice(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_ToSortedSlice")
set1 := FromSlice([]int{6, 3, 1, 5, 6, 7, 1})
set2 := FromSlice([]float64{-2.65, 4.25, 4.25 - 3.14, 0})
type Person struct {
Name string
Age int
}
set3 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
slice1 := set1.ToSortedSlice(func(v1, v2 int) bool {
return v1 < v2
})
slice2 := set2.ToSortedSlice(func(v1, v2 float64) bool {
return v2 < v1
})
slice3 := set3.ToSortedSlice(func(v1, v2 Person) bool {
return v1.Age < v2.Age
})
assert.Equal(5, len(slice1))
assert.Equal(4, len(slice2))
assert.Equal(3, len(slice3))
assert.Equal(true, reflect.DeepEqual(slice1, []int{1, 3, 5, 6, 7}))
assert.Equal(true, reflect.DeepEqual(slice2, []float64{4.25, 1.11, 0, -2.65}))
assert.Equal(true, reflect.DeepEqual(slice3, []Person{
{"Jerry", 18},
{"Tom", 20},
{"Spike", 25},
}))
}

View File

@@ -7,22 +7,22 @@ package datastructure
import (
"math"
"github.com/duke-git/lancet/v2/constraints"
"github.com/duke-git/lancet/v2/datastructure"
"github.com/duke-git/lancet/v2/lancetconstraints"
)
// BSTree is a binary search tree data structure in which each node has at most two children,
// which are referred to as the left child and the right child.
// In BSTree: leftNode < rootNode < rightNode
// type T should implements Compare function in lancetconstraints.Comparator interface.
// type T should implements Compare function in constraints.Comparator interface.
type BSTree[T any] struct {
root *datastructure.TreeNode[T]
comparator lancetconstraints.Comparator
comparator constraints.Comparator
}
// NewBSTree create a BSTree pointer
// param `comparator` is used to compare values in the tree
func NewBSTree[T any](rootData T, comparator lancetconstraints.Comparator) *BSTree[T] {
func NewBSTree[T any](rootData T, comparator constraints.Comparator) *BSTree[T] {
root := datastructure.NewTreeNode(rootData)
return &BSTree[T]{root, comparator}
}
@@ -87,7 +87,7 @@ func (t *BSTree[T]) HasSubTree(subTree *BSTree[T]) bool {
}
func hasSubTree[T any](superTreeRoot, subTreeRoot *datastructure.TreeNode[T],
comparator lancetconstraints.Comparator) bool {
comparator constraints.Comparator) bool {
result := false
if superTreeRoot != nil && subTreeRoot != nil {

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"math"
"github.com/duke-git/lancet/v2/constraints"
"github.com/duke-git/lancet/v2/datastructure"
"github.com/duke-git/lancet/v2/lancetconstraints"
)
func preOrderTraverse[T any](node *datastructure.TreeNode[T]) []T {
@@ -86,7 +86,7 @@ func levelOrderTraverse[T any](root *datastructure.TreeNode[T], traversal *[]T)
}
}
func insertTreeNode[T any](rootNode, newNode *datastructure.TreeNode[T], comparator lancetconstraints.Comparator) {
func insertTreeNode[T any](rootNode, newNode *datastructure.TreeNode[T], comparator constraints.Comparator) {
if comparator.Compare(newNode.Value, rootNode.Value) == -1 {
if rootNode.Left == nil {
rootNode.Left = newNode
@@ -103,7 +103,7 @@ func insertTreeNode[T any](rootNode, newNode *datastructure.TreeNode[T], compara
}
// todo, delete root node failed
func deleteTreeNode[T any](node *datastructure.TreeNode[T], data T, comparator lancetconstraints.Comparator) *datastructure.TreeNode[T] {
func deleteTreeNode[T any](node *datastructure.TreeNode[T], data T, comparator constraints.Comparator) *datastructure.TreeNode[T] {
if node == nil {
return nil
}
@@ -216,7 +216,7 @@ func calculateDepth[T any](node *datastructure.TreeNode[T], depth int) int {
return max(calculateDepth(node.Left, depth+1), calculateDepth(node.Right, depth+1))
}
func isSubTree[T any](superTreeRoot, subTreeRoot *datastructure.TreeNode[T], comparator lancetconstraints.Comparator) bool {
func isSubTree[T any](superTreeRoot, subTreeRoot *datastructure.TreeNode[T], comparator constraints.Comparator) bool {
if subTreeRoot == nil {
return true
}

View File

@@ -381,3 +381,12 @@ func TimestampNano(timezone ...string) int64 {
return t.UnixNano()
}
// TraceFuncTime: trace the func costed time,just call it at top of the func like `defer TraceFuncTime()()`
func TraceFuncTime() func() {
pre := time.Now()
return func() {
elapsed := time.Since(pre)
fmt.Println("Costs Time:\t", elapsed)
}
}

View File

@@ -43,12 +43,12 @@ import (
### <span id="BubbleSort">BubbleSort</span>
<p>冒泡排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<p>冒泡排序参数comparator需要实现包constraints.Comparator。</p>
<b>函数签名:</b>
```go
func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator)
func BubbleSort[T any](slice []T, comparator constraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/GNdv7Jg2Taj)</span></b>
@@ -91,12 +91,12 @@ func main() {
### <span id="InsertionSort">InsertionSort</span>
<p>插入排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<p>插入排序参数comparator需要实现包constraints.Comparator。</p>
<b>函数签名:</b>
```go
func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator)
func InsertionSort[T any](slice []T, comparator constraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/G5LJiWgJJW6)</span></b>
@@ -117,7 +117,7 @@ type people struct {
// PeopleAageComparator sort people slice by age field
type peopleAgeComparator struct{}
// Compare implements github.com/duke-git/lancet/lancetconstraints/constraints.go/Comparator
// Compare implements github.com/duke-git/lancet/constraints/constraints.go/Comparator
func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int {
p1, _ := v1.(people)
p2, _ := v2.(people)
@@ -154,12 +154,12 @@ func main() {
### <span id="SelectionSort">SelectionSort</span>
<p>选择排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<p>选择排序参数comparator需要实现包constraints.Comparator。</p>
<b>函数签名:</b>
```go
func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator)
func SelectionSort[T any](slice []T, comparator constraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/oXovbkekayS)</span></b>
@@ -202,12 +202,12 @@ func main() {
### <span id="ShellSort">ShellSort</span>
<p>希尔排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<p>希尔排序参数comparator需要实现包constraints.Comparator。</p>
<b>函数签名:</b>
```go
func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator)
func ShellSort[T any](slice []T, comparator constraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/3ibkszpJEu3)</span></b>
@@ -250,12 +250,12 @@ func main() {
### <span id="QuickSort">QuickSort</span>
<p>快速排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<p>快速排序参数comparator需要实现包constraints.Comparator。</p>
<b>函数签名:</b>
```go
func QuickSort[T any](slice []T comparator lancetconstraints.Comparator)
func QuickSort[T any](slice []T comparator constraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/7Y7c1Elk3ax)</span></b>
@@ -298,12 +298,12 @@ func main() {
### <span id="HeapSort">HeapSort</span>
<p>堆排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<p>堆排序参数comparator需要实现包constraints.Comparator。</p>
<b>函数签名:</b>
```go
func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator)
func HeapSort[T any](slice []T, comparator constraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/u6Iwa1VZS_f)</span></b>
@@ -346,12 +346,12 @@ func main() {
### <span id="MergeSort">MergeSort</span>
<p>归并排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<p>归并排序参数comparator需要实现包constraints.Comparator。</p>
<b>函数签名:</b>
```go
func MergeSort[T any](slice []T, comparator lancetconstraints.Comparator)
func MergeSort[T any](slice []T, comparator constraints.Comparator)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/ydinn9YzUJn)</span></b>
@@ -394,12 +394,12 @@ func main() {
### <span id="CountSort">CountSort</span>
<p>计数排序参数comparator需要实现包lancetconstraints.Comparator。</p>
<p>计数排序参数comparator需要实现包constraints.Comparator。</p>
<b>函数签名:</b>
```go
func CountSort[T any](slice []T, comparator lancetconstraints.Comparator) []T
func CountSort[T any](slice []T, comparator constraints.Comparator) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/tB-Umgm0DrP)</span></b>
@@ -443,12 +443,12 @@ func main() {
### <span id="BinarySearch">BinarySearch</span>
<p>二分递归查找,返回元素索引,未找到元素返回-1参数comparator需要实现包lancetconstraints.Comparator。</p>
<p>二分递归查找,返回元素索引,未找到元素返回-1参数comparator需要实现包constraints.Comparator。</p>
<b>函数签名:</b>
```go
func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int
func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/t6MeGiUSN47)</span></b>
@@ -494,12 +494,12 @@ func main() {
### <span id="BinaryIterativeSearch">BinaryIterativeSearch</span>
<p>二分迭代查找,返回元素索引,未找到元素返回-1参数comparator需要实现包lancetconstraints.Comparator。</p>
<p>二分迭代查找,返回元素索引,未找到元素返回-1参数comparator需要实现包constraints.Comparator。</p>
<b>函数签名:</b>
```go
func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int
func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int
```
<b>示例: <span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Anozfr8ZLH3)</span></b>

View File

@@ -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
}
```

View File

@@ -1437,7 +1437,7 @@ func main() {
func GenerateRsaKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey)
```
<b>示例:></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/sSVmkfENKMz)</span></b>
```go
package main
@@ -1462,7 +1462,7 @@ func main() {
func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error)
```
<b>示例:></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/sSVmkfENKMz)</span></b>
```go
package main
@@ -1505,7 +1505,7 @@ func main() {
func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte, error)
```
<b>示例:></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/sSVmkfENKMz)</span></b>
```go
package main

View File

@@ -26,6 +26,8 @@ import (
- [Remove](#Remove)
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [IndexOfFunc](#IndexOfFunc)
- [LastIndexOfFunc](#LastIndexOfFunc)
- [IsEmpty](#IsEmpty)
- [Contain](#Contain)
- [ValueOf](#ValueOf)
@@ -198,6 +200,58 @@ func main() {
```
### <span id="IndexOfFunc">IndexOfFunc</span>
<p>返回第一个满足判断函数f(v)的元素的索引,如果找不到则返回-1。</p>
<b>函数签名:</b>
```go
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1, 2, 3})
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 1 })) //0
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 0 })) //-1
}
```
### <span id="LastIndexOfFunc">LastIndexOfFunc</span>
<p>返回最后一个满足判断函数f(v)的元素的索引,如果找不到则返回-1。</p>
<b>函数签名:</b>
```go
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1, 2, 3, 1})
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1
}
```
### IsEmpty
如果此列表不包含任何元素,则返回 true。

View File

@@ -44,9 +44,9 @@ MaxHeap是通过slice实现的二叉堆树根节点的key既大于等于左
```go
type MaxHeap[T any] struct {
data []T
comparator lancetconstraints.Comparator
comparator constraints.Comparator
}
func NewMaxHeap[T any](comparator lancetconstraints.Comparator) *MaxHeap[T]
func NewMaxHeap[T any](comparator constraints.Comparator) *MaxHeap[T]
```
<b>示例:</b>

View File

@@ -0,0 +1,412 @@
# Optional
Optional类型代表一个可选的值它要么包含一个实际值要么为空。
<div STYLE="page-break-after: always;"></div>
## 源码
- [https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go](https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go)
<div STYLE="page-break-after: always;"></div>
## 用法
```go
import (
"github.com/duke-git/lancet/v2/datastructure/optional"
)
```
<div STYLE="page-break-after: always;"></div>
## 目录
- [Of](#Of)
- [FromNillable](#FromNillable)
- [Default](#Default)
- [IsNotNil](#IsNotNil)
- [IsNil](#IsNil)
- [IsNotNil](#IsNotNil)
- [IfNotNilOrElse](#IfNotNilOrElse)
- [Umwarp](#Umwarp)
- [OrElse](#OrElse)
- [OrElseGet](#OrElseGet)
- [OrElseTrigger](#OrElseTrigger)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="Of">Of</span>
<p>返回一个包含非空值的Optional。</p>
<b>函数签名:</b>
```go
func Of[T any](value T) Optional[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
value := 42
opt := optional.Of(value)
fmt.Println(opt.Get())
// Output:
// 42
}
```
### <span id="FromNillable">FromNillable</span>
<p>返回一个包含给定值的Optional该值可能为空 (nil)。</p>
<b>函数签名:</b>
```go
func FromNillable[T any](value *T) Optional[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
var value *int = nil
opt := optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
value = new(int)
*value = 42
opt = optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
// Output:
// false
// true
}
```
### <span id="Default">Default</span>
<p>返回一个空Optional实例。</p>
<b>函数签名:</b>
```go
func Default[T any]() Optional[T]
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
fmt.Println(optDefault.IsNil())
// Output:
// true
}
```
### <span id="IsNil">IsNil</span>
<p>验证Optional是否为空。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) IsNil() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
fmt.Println(optDefault.IsNil())
// Output:
// true
}
```
### <span id="IsNotNil">IsNotNil</span>
<p>检查当前Optional内是否存在值。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) IsNotNil() bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
var value *int = nil
opt := optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
value = new(int)
*value = 42
opt = optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
// Output:
// false
// true
}
```
### <span id="IfNotNil">IfNotNil</span>
<p>如果值存在则使用action方法执行给定的操作。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) IfNotNil(action func(value T))
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
called := false
action := func(value int) { called = true }
optDefault := optional.Default[int]()
optDefault.IfNotNil(action)
fmt.Println(called)
called = false // Reset for next test
optWithValue := optional.Of(42)
optWithValue.IfNotNil(action)
fmt.Println(optWithValue.IsNotNil())
// Output:
// false
// true
}
```
### <span id="IfNotNilOrElse">IfNotNilOrElse</span>
<p>根据是否存在值执行相应的操作:有值则执行指定操作,没有值则执行默认操作。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func())
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
calledWithValue := false
valueAction := func(value int) { calledWithValue = true }
emptyAction := func() { t.Errorf("Empty action should not be called when value is present") }
optWithValue := optional.Of(42)
optWithValue.IfNotNilOrElse(valueAction, emptyAction)
fmt.Println(calledWithValue)
calledWithEmpty := false
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
emptyAction = func() { calledWithEmpty = true }
optDefault := optional.Default[int]()
optDefault.IfNotNilOrElse(valueAction, emptyAction)
fmt.Println(calledWithEmpty)
// Output:
// true
// true
}
```
### <span id="Unwrap">Unwrap</span>
<p>如果存在返回该值否则引发panic。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) Unwrap() T
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
value := 42
opt := optional.Of(value)
fmt.Println(opt.Unwrap())
// Output:
// 42
}
```
### <span id="OrElse">OrElse</span>
<p>检查Optional值是否存在如果存在则直接返回该值。如果不存在返回参数other值。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) OrElse(other T) T
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Empty[int]()
val := optDefault.OrElse(100)
fmt.Println(val)
optWithValue := optional.Of(42)
val = optWithValue.OrElse(100)
fmt.Println(val)
// Output:
// 100
// 42
}
```
### <span id="OrElseGet">OrElseGet</span>
<p>检查Optional值是否存在如果存在则直接返回该值。如果不存在则调用一个提供的函数 (supplier),并返回该函数的执行结果。</p>
<b>函数签名:</b>
```go
func (o Optional[T]) OrElseGet(action func() T) T
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
action := func() int { return 100 }
val := optDefault.OrElseGet(action)
fmt.Println(val)
// Output:
// 100
}
```
### <span id="OrElseTrigger">OrElseTrigger</span>
<p>检查Optional值是否存在如果存在则直接返回该值否则返回错误。</p>
<b>函数签名:</b>
```go
OrElseTrigger(errorHandler func() error) (T, error)
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
fmt.Println(err.Error())
optWithValue := optional.Of(42)
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
fmt.Println(val)
fmt.Println(err)
// Output:
// no value
// 42
// nil
}
```

View File

@@ -1100,12 +1100,12 @@ func main() {
<b>函数签名:</b>
```go
func NewPriorityQueue[T any](capacity int, comparator lancetconstraints.Comparator) *PriorityQueue[T]
func NewPriorityQueue[T any](capacity int, comparator constraints.Comparator) *PriorityQueue[T]
type PriorityQueue[T any] struct {
items []T
size int
comparator lancetconstraints.Comparator
comparator constraints.Comparator
}
```
<b>示例:</b>

View File

@@ -1,6 +1,6 @@
# Set
Set 集合数据结构类似列表。Set 中元素不重复。
集合数据结构类似列表。Set中元素不重复。
<div STYLE="page-break-after: always;"></div>
@@ -22,8 +22,8 @@ import (
## 目录
- [NewSet](#NewSet)
- [NewSetFromSlice](#NewSetFromSlice)
- [New](#New)
- [FromSlice](#FromSlice)
- [Values](#Values)
- [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
@@ -40,20 +40,23 @@ import (
- [Intersection](#Intersection)
- [SymmetricDifference](#SymmetricDifference)
- [Minus](#Minus)
- [Pop](#Pop)
- [ToSlice](#ToSlice)
- [ToSortedSlice](#ToSortedSlice)
<div STYLE="page-break-after: always;"></div>
## 文档
### <span id="NewSet">NewSet</span>
### <span id="New">New</span>
<p>返回Set结构体对象</p>
<b>函数签名:</b>
```go
type Set[T comparable] map[T]bool
func NewSet[T comparable](items ...T) Set[T]
type Set[T comparable] map[T]struct{}
func New[T comparable](items ...T) Set[T]
```
<b>示例:</b>
@@ -67,19 +70,19 @@ import (
)
func main() {
st := set.NewSet[int](1,2,2,3)
st := set.New[int](1,2,2,3)
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="NewSetFromSlice">NewSetFromSlice</span>
### <span id="FromSlice">FromSlice</span>
<p>基于切片创建集合</p>
<b>函数签名:</b>
```go
func NewSetFromSlice[T comparable](items []T) Set[T]
func FromSlice[T comparable](items []T) Set[T]
```
<b>示例:</b>
@@ -93,14 +96,15 @@ import (
)
func main() {
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
st := set.FromSlice([]int{1, 2, 2, 3})
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="Values">Values</span>
### <span id="Values">Values<sup>deprecated</sup></span>
<p>获取集合中所有元素的切片</p>
<p>获取集合中所有元素的切片<br>
<a href='#ToSlice'>ToSlice()</a> 方法提供与 Values 方法相同的功能</p>
<b>函数签名:</b>
@@ -119,7 +123,7 @@ import (
)
func main() {
st := set.NewSet[int](1,2,2,3)
st := set.New[int](1,2,2,3)
fmt.Println(st.Values()) //1,2,3
}
```
@@ -145,7 +149,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
fmt.Println(st.Values()) //1,2,3
@@ -173,7 +177,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
r1 := st.AddIfNotExist(1)
@@ -206,7 +210,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2)
ok := st.AddIfNotExistBy(3, func(val int) bool {
@@ -245,7 +249,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
set.Delete(3)
@@ -274,7 +278,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
fmt.Println(st.Contain(1)) //true
@@ -303,9 +307,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(1, 2)
set3 := set.NewSet(1, 2, 3, 4)
set1 := set.New(1, 2, 3)
set2 := set.New(1, 2)
set3 := set.New(1, 2, 3, 4)
fmt.Println(set1.ContainAll(set2)) //true
fmt.Println(set1.ContainAll(set3)) //false
@@ -333,7 +337,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
fmt.Println(set1.Size()) //3
}
@@ -360,7 +364,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
set2 := set1.Clone()
fmt.Println(set1.Size() == set2.Size()) //true
@@ -389,9 +393,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(1, 2, 3)
set3 := set.NewSet(1, 2, 3, 4)
set1 := set.New(1, 2, 3)
set2 := set.New(1, 2, 3)
set3 := set.New(1, 2, 3, 4)
fmt.Println(set1.Equal(set2)) //true
fmt.Println(set1.Equal(set3)) //false
@@ -419,7 +423,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
arr := []int{}
set.Iterate(func(item int) {
arr = append(arr, item)
@@ -450,7 +454,7 @@ import (
)
func main() {
s := set.NewSet(1, 2, 3, 4, 5)
s := set.New(1, 2, 3, 4, 5)
var sum int
@@ -487,8 +491,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet()
set1 := set.New(1, 2, 3)
set2 := set.New()
fmt.Println(set1.IsEmpty()) //false
fmt.Println(set2.IsEmpty()) //true
@@ -516,8 +520,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.Union(set2)
fmt.Println(set3.Values()) //1,2,3,4,5
@@ -545,8 +549,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.Intersection(set2)
fmt.Println(set3.Values()) //2,3
@@ -574,8 +578,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.SymmetricDifference(set2)
fmt.Println(set3.Values()) //1,4,5
@@ -603,9 +607,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set3 := set.NewSet(2, 3)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set.New(2, 3)
res1 := set1.Minus(set2)
fmt.Println(res1.Values()) //1
@@ -636,7 +640,7 @@ import (
)
func main() {
s := set.NewSet[int]()
s := set.New[int]()
s.Add(1)
s.Add(2)
s.Add(3)
@@ -647,3 +651,58 @@ func main() {
fmt.Println(ok) // true
}
```
### <span id="ToSlice">ToSlice</span>
<p>以切片的形式返回集合中所有的元素(无序)</p>
<b>函数签名:</b>
```go
func (s Set[T]) ToSlice() (v T, ok bool)
```
<b>示例:</b>
```go
func main() {
s := set.New(1, 2, 3, 4, 5)
val := s.ToSlice()
fmt.Println(val) // [2 3 4 5 1]
}
```
### <span id="ToSortedSlice">ToSortedSlice</span>
<p>以切片的形式返回集合中所有的元素(按给定的规则排序)</p>
<b>函数签名:</b>
```go
func (s Set[T]) ToSortedSlice() (v T, ok bool)
```
<b>示例:</b>
```go
func main() {
s1 := set.New(1, 2, 3, 4, 5)
type Person struct {
Name string
Age int
}
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
return v1 < v2
})
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
return v1.Age < v2.Age
})
fmt.Println(res1) // [1 2 3 4 5]
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
}
```

View File

@@ -41,7 +41,7 @@ import (
## 文档
## 1. BSTree
BSTree是一种二叉搜索树数据结构其中每个节点有两个孩子分别称为左孩子和右孩子。 在 BSTree 中leftNode < rootNode < rightNode。 T类型应该实现lancetconstraints.Comparator。
BSTree是一种二叉搜索树数据结构其中每个节点有两个孩子分别称为左孩子和右孩子。 在 BSTree 中leftNode < rootNode < rightNode。 T类型应该实现constraints.Comparator。
### <span id="NewBSTree">NewBSTree</span>
<p>返回BSTree指针实例</p>
@@ -49,11 +49,11 @@ BSTree是一种二叉搜索树数据结构其中每个节点有两个孩子
<b>函数签名:</b>
```go
func NewBSTree[T any](rootData T, comparator lancetconstraints.Comparator) *BSTree[T]
func NewBSTree[T any](rootData T, comparator constraints.Comparator) *BSTree[T]
type BSTree[T any] struct {
root *datastructure.TreeNode[T]
comparator lancetconstraints.Comparator
comparator constraints.Comparator
}
type TreeNode[T any] struct {

View File

@@ -26,6 +26,7 @@ import (
- [CreateFile](#CreateFile)
- [CreateDir](#CreateDir)
- [CopyFile](#CopyFile)
- [CopyDir](#CopyDir)
- [CurrentPath](#CurrentPath)
- [FileMode](#FileMode)
- [MiMeType](#MiMeType)
@@ -45,9 +46,12 @@ import (
- [Sha](#Sha)
- [ReadCsvFile](#ReadCsvFile)
- [WriteCsvFile](#WriteCsvFile)
- [WriteMapsToCsv](#WriteMapsToCsv)
- [WriteStringToFile](#WriteStringToFile)
- [WriteBytesToFile](#WriteBytesToFile)
- [ReadFile](#ReadFile)
- [ChunkRead](#ChunkRead)
- [ParallelChunkRead](#ParallelChunkRead)
<div STYLE="page-break-after: always;"></div>
@@ -161,6 +165,34 @@ func main() {
}
```
### <span id="CopyDir">CopyDir</span>
<p>拷贝文件夹到目标路径会递归复制文件夹下所有的文件及文件夹并且访问权限也与源文件夹保持一致。当dstPath存在时会返回error</p>
<b>函数签名:</b>
```go
func CopyDir(srcPath string, dstPath string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/YAyFTA_UuPb)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.CopyFile("./test_src", "./test_dest")
if err != nil {
fmt.Println(err)
}
}
```
### <span id="CurrentPath">CurrentPath</span>
<p>返回当前位置的绝对路径。</p>
@@ -669,7 +701,7 @@ func main() {
<b>函数签名:</b>
```go
func ReadCsvFile(filepath string) ([][]string, error)
func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/OExTkhGEd3_u)</span></b>
@@ -701,7 +733,7 @@ func main() {
<b>函数签名:</b>
```go
func WriteCsvFile(filepath string, records [][]string, append bool) error
func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dAXm58Q5U1o)</span></b>
@@ -743,6 +775,59 @@ func main() {
}
```
### <span id="WriteMapsToCsv">WriteMapsToCsv</span>
<p>将map切片写入csv文件中。</p>
<b>函数签名:</b>
```go
// filepath: CSV文件路径。
// records: 写入文件的map切片。map值必须为基本类型。会以map键的字母顺序写入。
// appendToExistingFile: 是否为追加写模式。
// delimiter: CSV文件分割符。
// headers: CSV文件表头顺序需要与map key保持一致),不指定时按字母排序。
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/umAIomZFV1c)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
fpath := "./test.csv"
fileutil.CreateFile(fpath)
f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
records := []map[string]any{
{"Name": "Lili", "Age": "22", "Gender": "female"},
{"Name": "Jim", "Age": "21", "Gender": "male"},
}
headers := []string{"Name", "Age", "Gender"}
err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers)
if err != nil {
log.Fatal(err)
}
content, err := fileutil.ReadCsvFile(csvFilePath, ';')
fmt.Println(content)
// Output:
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
}
```
### <span id="WriteBytesToFile">WriteBytesToFile</span>
<p>将bytes写入文件。</p>
@@ -843,7 +928,7 @@ func main() {
### <span id="ReadFile">ReadFile</span>
<p>读取文件或者URL</p>
<p>读取文件或者URL</p>
<b>函数签名:</b>
@@ -851,7 +936,7 @@ func main() {
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error)
```
<b>示例:<span style="float:right;display:inline-block;"></span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/uNep3Tr8fqF)</span></b>
```go
package main
@@ -862,19 +947,133 @@ import (
)
func main() {
reader, fn, err := ReadFile("https://httpbin.org/robots.txt")
if err != nil {
return
}
defer fn()
reader, fn, err := fileutil.ReadFile("https://httpbin.org/robots.txt")
if err != nil {
return
}
defer fn()
dat, err := io.ReadAll(reader)
if err != nil {
return
}
fmt.Println(string(dat))
// Output:
// User-agent: *
// Disallow: /deny
dat, err := io.ReadAll(reader)
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
}
```

View File

@@ -7,6 +7,7 @@ function 函数包控制函数执行流程,包含部分函数式编程。
## 源码:
- [https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go)
- [https://github.com/duke-git/lancet/blob/main/function/predicate.go](https://github.com/duke-git/lancet/blob/main/function/predicate.go)
- [https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go)
<div STYLE="page-break-after: always;"></div>
@@ -32,6 +33,14 @@ import (
- [Schedule](#Schedule)
- [Pipeline](#Pipeline)
- [Watcher](#Watcher)
- [And](#And)
- [Or](#Or)
- [Negate](#Negate)
- [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand)
- [AcceptIf](#AcceptIf)
<div STYLE="page-break-after: always;"></div>
@@ -404,3 +413,282 @@ func longRunningTask() {
}
```
### <span id="And">And</span>
<p>返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑and操作。只有当所有谓词判断函数对于给定的值都返回true时返回true, 否则返回false。</p>
<b>函数签名:</b>
```go
func And[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isNumericAndLength5 := function.And(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcde"))
// Output:
// true
// false
// false
}
```
### <span id="Or">Or</span>
<p>返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑or操作。只有当所有谓词判断函数对于给定的值都返回false时返回false, 否则返回true。</p>
<b>函数签名:</b>
```go
func Or[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
containsDigitOrSpecialChar := function.Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
)
fmt.Println(containsDigitOrSpecialChar("hello!"))
fmt.Println(containsDigitOrSpecialChar("hello"))
// Output:
// true
// false
}
```
### <span id="Negate">Negate</span>
<p>返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。</p>
<b>函数签名:</b>
```go
func Negate[T any](predicate func(T) bool) func(T) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
// Define some simple predicates for demonstration
isUpperCase := func(s string) bool {
return strings.ToUpper(s) == s
}
isLowerCase := func(s string) bool {
return strings.ToLower(s) == s
}
isMixedCase := function.Negate(function.Or(isUpperCase, isLowerCase))
fmt.Println(isMixedCase("ABC"))
fmt.Println(isMixedCase("AbC"))
// Output:
// false
// true
}
```
### <span id="Nor">Nor</span>
<p>返回一个组合谓词函数,表示给定值上所有谓词逻辑非或 (nor) 的结果。只有当所有谓词函数对给定值都返回false时该组合谓词函数才返回true。</p>
<b>函数签名:</b>
```go
func Nor[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
match := function.Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("dbcdckkeee"))
match = function.Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("0123456789"))
// Output:
// true
// false
}
```
### <span id="Nand">Nand</span>
<p>返回一个复合谓词函数,表示给定谓词函数列表的逻辑非与 (NAND)。仅当列表中所有函数对给定参数返回false时才返回true否则返回false。</p>
<b>函数签名:</b>
```go
func Nand[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isNumericAndLength5 := function.Nand(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcdef"))
// Output:
// false
// false
// true
}
```
### <span id="Xnor">Xnor</span>
<p>返回一个复合谓词函数,表示给定一组谓词函数的逻辑异或 (XNOR)。只有当所有 谓词函数对给参数都返回true或false时该谓词函数才返回true。</p>
<b>函数签名:</b>
```go
func Xnor[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isEven := func(i int) bool { return i%2 == 0 }
isPositive := func(i int) bool { return i > 0 }
match := function.Xnor(isEven, isPositive)
fmt.Println(match(2))
fmt.Println(match(-3))
fmt.Println(match(3))
// Output:
// true
// true
// false
}
```
### <span id="AcceptIf">AcceptIf</span>
<p>AcceptIf函数会返回另一个函数该函数的签名与 apply 函数相同,但同时还会包含一个布尔值来表示成功或失败。</p>
<b>函数签名:</b>
```go
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
```
<b>示例:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
adder := AcceptIf(
And(
func(x int) bool {
return x > 10
}, func(x int) bool {
return x%2 == 0
}),
func(x int) int {
return x + 1
},
)
result, ok := adder(20)
fmt.Println(result)
fmt.Println(ok)
result, ok = adder(21)
fmt.Println(result)
fmt.Println(ok)
// Output:
// 21
// true
// 0
// false
}
```

View File

@@ -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
}
```

View File

@@ -29,8 +29,11 @@ import (
- [RandLower](#RandLower)
- [RandNumeral](#RandNumeral)
- [RandNumeralOrLetter](#RandNumeralOrLetter)
- [RandSymbolChar](#RandSymbolChar)
- [UUIdV4](#UUIdV4)
- [RandUniqueIntSlice](#RandUniqueIntSlice)
- [RandFloat](#RandFloat)
- [RandFloats](#RandFloats)
<div STYLE="page-break-after: always;"></div>
@@ -218,6 +221,32 @@ func main() {
}
```
### <span id="RandSymbolChar">RandSymbolChar</span>
<p>生成给定长度的随机符号字符串。</p>
<b>函数签名:</b>
```go
func RandSymbolChar(length int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Im6ZJxAykOm)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randStr := random.RandSymbolChar(6)
fmt.Println(randStr) // 随机特殊字符字符串,例如: @#(_")
}
```
### <span id="UUIdV4">UUIdV4</span>
<p>生成UUID v4字符串</p>
@@ -272,3 +301,55 @@ func main() {
fmt.Println(result) //[0 4 7 1 5] (random)
}
```
### <span id="RandFloat">RandFloat</span>
<p>生成随机float64数字可以指定范围和精度。</p>
<b>函数签名:</b>
```go
func RandFloat(min, max float64, precision int) float64
```
<b>实例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/zbD_tuobJtr)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
floatNumber := random.RandFloat(1.0, 5.0, 2)
fmt.Println(floatNumber) //2.14 (random number)
}
```
### <span id="RandFloats">RandFloats</span>
<p>生成随机float64数字切片指定长度范围和精度.</p>
<b>函数签名:</b>
```go
func RandFloats(n int, min, max float64, precision int) []float64
```
<b>实例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
fmt.Println(floatNumber) //[3.42 3.99 1.3 2.38 4.23] (random)
}
```

View File

@@ -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
}
```

View File

@@ -35,6 +35,7 @@ import (
- [DifferenceBy](#DifferenceBy)
- [DifferenceWith](#DifferenceWith)
- [DeleteAt](#DeleteAt)
- [DeleteRange](#DeleteRange)
- [Drop](#Drop)
- [DropRight](#DropRight)
- [DropWhile](#DropWhile)
@@ -92,6 +93,7 @@ import (
- [KeyBy](#KeyBy)
- [Join](#Join)
- [Partition](#Partition)
- [SetToDefaultIf](#SetToDefaultIf)
<div STYLE="page-break-after: always;"></div>
@@ -516,15 +518,15 @@ func main() {
### <span id="DeleteAt">DeleteAt</span>
<p>删除切片中指定开始索引到结束索引的元素</p>
<p>删除切片中指定索引的元素(不修改原切片)。</p>
<b>函数签名:</b>
```go
func DeleteAt[T any](slice []T, start int, end ...int)
func DeleteAt[T any](slice []T, index int) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/pJ-d6MUWcvK)</span></b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/800B1dPBYyd)</span></b>
```go
import (
@@ -533,18 +535,66 @@ import (
)
func main() {
result1 := slice.DeleteAt([]string{"a", "b", "c"}, -1)
result2 := slice.DeleteAt([]string{"a", "b", "c"}, 0)
result3 := slice.DeleteAt([]string{"a", "b", "c"}, 0, 2)
chars := []string{"a", "b", "c", "d", "e"}
result1 := slice.DeleteAt(chars, 0)
result2 := slice.DeleteAt(chars, 4)
result3 := slice.DeleteAt(chars, 5)
result4 := slice.DeleteAt(chars, 6)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// [a b c]
// [b c]
// [c]
// [b c d e]
// [a b c d]
// [a b c d]
// [a b c d]
}
```
### <span id="DeleteRange">DeleteRange</span>
<p>删除切片中指定索引范围的元素(不修改原切片)。</p>
<b>函数签名:</b>
```go
func DeleteRange[T any](slice []T, start, end int) []T
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/945HwiNrnle)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
chars := []string{"a", "b", "c", "d", "e"}
result1 := DeleteRange(chars, 0, 0)
result2 := DeleteRange(chars, 0, 1)
result3 := DeleteRange(chars, 0, 3)
result4 := DeleteRange(chars, 0, 4)
result5 := DeleteRange(chars, 0, 5)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
// Output:
// [a b c d e]
// [b c d e]
// [d e]
// [e]
// []
}
```
@@ -2475,17 +2525,79 @@ import (
func main() {
nums := []int{1, 2, 3, 4, 5}
result1 := slice.Partition(nums)
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
result1 := slice.Partition(nums)
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// [[1 2 3 4 5]]
// [[2 4] [1 3 5]]
// [[1 2] [3 4] [5]]
// Output:
// [[1 2 3 4 5]]
// [[2 4] [1 3 5]]
// [[1 2] [3 4] [5]]
}
```
### <span id="Random">Random</span>
<p>随机返回切片中元素以及下标, 当切片长度为0时返回下标-1</p>
<b>函数签名:</b>
```go
func Random[T any](slice []T) (val T, idx int)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/UzpGQptWppw)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
val, idx := slice.Random(nums)
if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) {
fmt.Println("okk")
}
// 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
}
```

View File

@@ -37,6 +37,7 @@ import (
- [IsExported](#IsExported)
- [IsZero](#IsZero)
- [IsSlice](#IsSlice)
- [IsTargetType](#IsTargetType)
<div STYLE="page-break-after: always;"></div>
@@ -531,4 +532,45 @@ func main() {
// Output:
// true
}
```
### <span id="IsTargetType">IsTargetType</span>
<p>判断属性是否是目标类型</p>
<b>函数签名:</b>
```go
func (f *Field) IsTargetType(targetType reflect.Kind) bool
```
<b>示例:</b>
```go
package main
import (
"fmt"
"reflect"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string
arr []int
}
p1 := &Parent{arr: []int{1, 2, 3}}
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("arr")
fmt.Println(n.IsTargetType(reflect.String))
fmt.Println(a.IsTargetType(reflect.Slice))
// Output:
// true
// true
}
```

View File

@@ -60,6 +60,8 @@ import (
- [ContainsAll](#ContainsAll)
- [ContainsAny](#ContainsAny)
- [RemoveWhiteSpace](#RemoveWhiteSpace)
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
<div STYLE="page-break-after: always;"></div>
@@ -1080,13 +1082,13 @@ func main() {
<p>Checks if a string is not whitespace or not empty.</p>
<b>Signature:</b>
<b>函数签名:</b>
```go
func IsNotBlank(str string) bool
```
<b>Example:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/e_oJW0RAquA)</span></b>
```go
import (
@@ -1095,11 +1097,11 @@ import (
)
func main() {
result1 := IsNotBlank("")
result2 := IsNotBlank(" ")
result3 := IsNotBlank("\t\v\f\n")
result4 := IsNotBlank(" 中文")
result5 := IsNotBlank(" world ")
result1 := strutil.IsNotBlank("")
result2 := strutil.IsNotBlank(" ")
result3 := strutil.IsNotBlank("\t\v\f\n")
result4 := strutil.IsNotBlank(" 中文")
result5 := strutil.IsNotBlank(" world ")
fmt.Println(result1)
fmt.Println(result2)
@@ -1462,3 +1464,68 @@ func main() {
// hello world
}
```
### <span id="SubInBetween">SubInBetween</span>
<p>获取字符串中指定的起始字符串start和终止字符串end直接的子字符串。</p>
<b>函数签名:</b>
```go
func SubInBetween(str string, start string, end string) string
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
str := "abcde"
result1 := strutil.SubInBetween(str, "", "de")
result2 := strutil.SubInBetween(str, "a", "d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// abc
// bc
}
```
### <span id="HammingDistance">HammingDistance</span>
<p>计算两个字符串之间的汉明距离。汉明距离是指对应符号不同的位置数。</p>
<b>函数签名:</b>
```go
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
}
```

View File

@@ -43,12 +43,12 @@ import (
### <span id="BubbleSort">BubbleSort</span>
<p>Sort slice with bubble sort algorithm. Param comparator should implements lancetconstraints.Comparator.</p>
<p>Sort slice with bubble sort algorithm. Param comparator should implements constraints.Comparator.</p>
<b>Signature:</b>
```go
func BubbleSort[T any](slice []T, comparator lancetconstraints.Comparator)
func BubbleSort[T any](slice []T, comparator constraints.Comparator)
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/GNdv7Jg2Taj)</span></b>
@@ -91,12 +91,12 @@ func main() {
### <span id="InsertionSort">InsertionSort</span>
<p>Sort slice with insertion sort algorithm. Param comparator should implements lancetconstraints.Comparator.</p>
<p>Sort slice with insertion sort algorithm. Param comparator should implements constraints.Comparator.</p>
<b>Signature:</b>
```go
func InsertionSort[T any](slice []T, comparator lancetconstraints.Comparator)
func InsertionSort[T any](slice []T, comparator constraints.Comparator)
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/G5LJiWgJJW6)</span></b>
@@ -117,7 +117,7 @@ type people struct {
// PeopleAageComparator sort people slice by age field
type peopleAgeComparator struct{}
// Compare implements github.com/duke-git/lancet/lancetconstraints/constraints.go/Comparator
// Compare implements github.com/duke-git/lancet/constraints/constraints.go/Comparator
func (pc *peopleAgeComparator) Compare(v1 any, v2 any) int {
p1, _ := v1.(people)
p2, _ := v2.(people)
@@ -154,12 +154,12 @@ func main() {
### <span id="SelectionSort">SelectionSort</span>
<p>Sort slice with selection sort algorithm. Param comparator should implements lancetconstraints.Comparator.</p>
<p>Sort slice with selection sort algorithm. Param comparator should implements constraints.Comparator.</p>
<b>Signature:</b>
```go
func SelectionSort[T any](slice []T, comparator lancetconstraints.Comparator)
func SelectionSort[T any](slice []T, comparator constraints.Comparator)
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/oXovbkekayS)</span></b>
@@ -202,12 +202,12 @@ func main() {
### <span id="ShellSort">ShellSort</span>
<p>Sort slice with shell sort algorithm. Param comparator should implements lancetconstraints.Comparator.</p>
<p>Sort slice with shell sort algorithm. Param comparator should implements constraints.Comparator.</p>
<b>Signature:</b>
```go
func ShellSort[T any](slice []T, comparator lancetconstraints.Comparator)
func ShellSort[T any](slice []T, comparator constraints.Comparator)
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/3ibkszpJEu3)</span></b>
@@ -250,12 +250,12 @@ func main() {
### <span id="QuickSort">QuickSort</span>
<p>Sort slice with quick sort algorithm. Param comparator should implements lancetconstraints.Comparator.</p>
<p>Sort slice with quick sort algorithm. Param comparator should implements constraints.Comparator.</p>
<b>Signature:</b>
```go
func QuickSort[T any](slice []T comparator lancetconstraints.Comparator)
func QuickSort[T any](slice []T comparator constraints.Comparator)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/7Y7c1Elk3ax)</span></b>
@@ -298,12 +298,12 @@ func main() {
### <span id="HeapSort">HeapSort</span>
<p>Sort slice with heap sort algorithm. Param comparator should implements lancetconstraints.Comparator.</p>
<p>Sort slice with heap sort algorithm. Param comparator should implements constraints.Comparator.</p>
<b>Signature:</b>
```go
func HeapSort[T any](slice []T, comparator lancetconstraints.Comparator)
func HeapSort[T any](slice []T, comparator constraints.Comparator)
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/u6Iwa1VZS_f)</span></b>
@@ -346,12 +346,12 @@ func main() {
### <span id="MergeSort">MergeSort</span>
<p>Sort slice with merge sort algorithm. Param comparator should implements lancetconstraints.Comparator.</p>
<p>Sort slice with merge sort algorithm. Param comparator should implements constraints.Comparator.</p>
<b>Signature:</b>
```go
func MergeSort[T any](slice []T, comparator lancetconstraints.Comparator)
func MergeSort[T any](slice []T, comparator constraints.Comparator)
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/ydinn9YzUJn)</span></b>
@@ -394,12 +394,12 @@ func main() {
### <span id="CountSort">CountSort</span>
<p>Sort slice with count sort algorithm. Param comparator should implements lancetconstraints.Comparator.</p>
<p>Sort slice with count sort algorithm. Param comparator should implements constraints.Comparator.</p>
<b>Signature:</b>
```go
func CountSort[T any](slice []T, comparator lancetconstraints.Comparator) []T
func CountSort[T any](slice []T, comparator constraints.Comparator) []T
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/tB-Umgm0DrP)</span></b>
@@ -448,7 +448,7 @@ func main() {
<b>Signature:</b>
```go
func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int
func BinarySearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/t6MeGiUSN47)</span></b>
@@ -499,7 +499,7 @@ func main() {
<b>Signature:</b>
```go
func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator lancetconstraints.Comparator) int
func BinaryIterativeSearch[T any](sortedSlice []T, target T, lowIndex, highIndex int, comparator constraints.Comparator) int
```
<b>Example: <span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Anozfr8ZLH3)</span></b>

View File

@@ -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
}
```

View File

@@ -1437,7 +1437,7 @@ func main() {
func GenerateRsaKeyPair(keySize int) (*rsa.PrivateKey, *rsa.PublicKey)
```
<b>Example:></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/sSVmkfENKMz)</span></b>
```go
package main
@@ -1462,7 +1462,7 @@ func main() {
func RsaEncryptOAEP(data []byte, label []byte, key rsa.PublicKey) ([]byte, error)
```
<b>Example:></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/sSVmkfENKMz)</span></b>
```go
package main
@@ -1505,7 +1505,7 @@ func main() {
func RsaDecryptOAEP(ciphertext []byte, label []byte, key rsa.PrivateKey) ([]byte, error)
```
<b>Example:></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/sSVmkfENKMz)</span></b>
```go
package main

View File

@@ -26,6 +26,8 @@ import (
- [Remove](#Remove)
- [IndexOf](#IndexOf)
- [LastIndexOf](#LastIndexOf)
- [IndexOfFunc](#IndexOfFunc)
- [LastIndexOfFunc](#LastIndexOfFunc)
- [IsEmpty](#IsEmpty)
- [Contain](#Contain)
- [ValueOf](#ValueOf)
@@ -197,6 +199,59 @@ func main() {
```
### <span id="IndexOfFunc">IndexOfFunc</span>
<p> IndexOfFunc returns the first index satisfying the functional predicate f(v) bool. if not found return -1.</p>
<b>Signature:</b>
```go
func (l *CopyOnWriteList[T]) IndexOfFunc(f func(T) bool) int
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1, 2, 3})
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 1 })) //0
fmt.Println(l.IndexOfFunc(func(a int) bool { return a == 0 })) //-1
}
```
### <span id="LastIndexOfFunc">LastIndexOfFunc</span>
<p>LastIndexOfFunc returns the index of the last occurrence of the value in this list satisfying the functional predicate f(T) bool. if not found return -1.</p>
<b>Signature:</b>
```go
func (l *CopyOnWriteList[T]) LastIndexOfFunc(f func(T) bool) int
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/list"
)
func main() {
l := list.NewCopyOnWriteList([]int{1, 2, 3, 1})
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 1 })) // 3
fmt.Println(l.LastIndexOfFunc(func(a int) bool { return a == 0 })) //-1
}
```
### IsEmpty
Returns true if this list does not contain any elements.

View File

@@ -44,9 +44,9 @@ MaxHeap is a binary heap tree implemented by slice, The key of the root node is
```go
type MaxHeap[T any] struct {
data []T
comparator lancetconstraints.Comparator
comparator constraints.Comparator
}
func NewMaxHeap[T any](comparator lancetconstraints.Comparator) *MaxHeap[T]
func NewMaxHeap[T any](comparator constraints.Comparator) *MaxHeap[T]
```
<b>Example:</b>

View File

@@ -0,0 +1,416 @@
# Optional
Optional is a type that may or may not contain a non-nil value.
<div STYLE="page-break-after: always;"></div>
## Source
- [https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go](https://github.com/duke-git/lancet/blob/main/datastructure/optional/optional.go)
<div STYLE="page-break-after: always;"></div>
## Usage
```go
import (
"github.com/duke-git/lancet/v2/datastructure/optional"
)
```
<div STYLE="page-break-after: always;"></div>
## Index
- [Of](#Of)
- [FromNillable](#FromNillable)
- [Default](#Default)
- [IsNotNil](#IsNotNil)
- [IsNil](#IsNil)
- [IsNotNil](#IsNotNil)
- [IfNotNilOrElse](#IfNotNilOrElse)
- [Umwarp](#Umwarp)
- [OrElse](#OrElse)
- [OrElseGet](#OrElseGet)
- [OrElseTrigger](#OrElseTrigger)
<div STYLE="page-break-after: always;"></div>
## Documentation
### <span id="Of">Of</span>
<p>Returns an Optional with a non-nil value.</p>
<b>Signature:</b>
```go
func Of[T any](value T) Optional[T]
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
value := 42
opt := optional.Of(value)
fmt.Println(opt.Get())
// Output:
// 42
}
```
### <span id="FromNillable">FromNillable</span>
<p>Returns an Optional for a given value, which may be nil.</p>
<b>Signature:</b>
```go
func FromNillable[T any](value *T) Optional[T]
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
var value *int = nil
opt := optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
value = new(int)
*value = 42
opt = optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
// Output:
// false
// true
}
```
### <span id="Default">Default</span>
<p>Returns an default Optional instance.</p>
<b>Signature:</b>
```go
func Default[T any]() Optional[T]
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
fmt.Println(optDefault.IsNil())
// Output:
// true
}
```
### <span id="IsNil">IsNil</span>
<p>Checks if the Optional is nil.</p>
<b>Signature:</b>
```go
func (o Optional[T]) IsNil() bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
fmt.Println(optDefault.IsNil())
// Output:
// true
}
```
### <span id="IsNotNil">IsNotNil</span>
<p>Checks if there is a value not nil.</p>
<b>Signature:</b>
```go
func (o Optional[T]) IsNotNil() bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
var value *int = nil
opt := optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
value = new(int)
*value = 42
opt = optional.FromNillable(value)
fmt.Println(opt.IsNotNil())
// Output:
// false
// true
}
```
### <span id="IfNotNil">IfNotNil</span>
<p>Performs the given action with the value if a value is present.</p>
<b>Signature:</b>
```go
func (o Optional[T]) IfNotNil(action func(value T))
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
called := false
action := func(value int) { called = true }
optDefault := optional.Default[int]()
optDefault.IfNotNil(action)
fmt.Println(called)
called = false // Reset for next test
optWithValue := optional.Of(42)
optWithValue.IfNotNil(action)
fmt.Println(optWithValue.IsNotNil())
// Output:
// false
// true
}
```
### <span id="IfNotNilOrElse">IfNotNilOrElse</span>
<p>Performs the action with the value if not nil, otherwise performs the fallback action.</p>
<b>Signature:</b>
```go
func (o Optional[T]) IfNotNilOrElse(action func(value T), fallbackAction func())
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
calledWithValue := false
valueAction := func(value int) { calledWithValue = true }
emptyAction := func() { t.Errorf("Empty action should not be called when value is present") }
optWithValue := optional.Of(42)
optWithValue.IfNotNilOrElse(valueAction, emptyAction)
fmt.Println(calledWithValue)
calledWithEmpty := false
valueAction = func(value int) { t.Errorf("Value action should not be called when value is not present") }
emptyAction = func() { calledWithEmpty = true }
optDefault := optional.Default[int]()
optDefault.IfNotNilOrElse(valueAction, emptyAction)
fmt.Println(calledWithEmpty)
// Output:
// true
// true
}
```
### <span id="Unwrap">Unwrap</span>
<p>Returns the value if not nil, otherwise panics.</p>
<b>Signature:</b>
```go
func (o Optional[T]) Unwrap() T
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
value := 42
opt := optional.Of(value)
fmt.Println(opt.Unwrap())
// Output:
// 42
}
```
### <span id="OrElse">OrElse</span>
<p>Returns the value if not nill, otherwise returns other.</p>
<b>Signature:</b>
```go
func (o Optional[T]) OrElse(other T) T
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
val := optDefault.OrElse(100)
fmt.Println(val)
optWithValue := optional.Of(42)
val = optWithValue.OrElse(100)
fmt.Println(val)
// Output:
// 100
// 42
}
```
### <span id="OrElseGet">OrElseGet</span>
<p>Returns the value if not nil, otherwise invokes action and returns the result.</p>
<b>Signature:</b>
```go
func (o Optional[T]) OrElseGet(action func() T) T
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
action := func() int { return 100 }
val := optDefault.OrElseGet(action)
fmt.Println(val)
// Output:
// 100
}
```
### <span id="OrElseTrigger">OrElseTrigger</span>
<p>Returns the value if present, otherwise returns an error.</p>
<b>Signature:</b>
```go
OrElseTrigger(errorHandler func() error) (T, error)
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/datastructure/optional"
)
func main() {
optDefault := optional.Default[int]()
_, err := optDefault.OrElseTrigger(func() error { return errors.New("no value") })
fmt.Println(err.Error())
optWithValue := optional.Of(42)
val, err := optWithValue.OrElseTrigger(func() error { return errors.New("no value") })
fmt.Println(val)
fmt.Println(err)
// Output:
// no value
// 42
// nil
}
```

View File

@@ -1100,12 +1100,12 @@ Common queue implemented by slice.
<b>Signature:</b>
```go
func NewPriorityQueue[T any](capacity int, comparator lancetconstraints.Comparator) *PriorityQueue[T]
func NewPriorityQueue[T any](capacity int, comparator constraints.Comparator) *PriorityQueue[T]
type PriorityQueue[T any] struct {
items []T
size int
comparator lancetconstraints.Comparator
comparator constraints.Comparator
}
```
<b>Example:</b>

View File

@@ -22,8 +22,8 @@ import (
## Index
- [NewSet](#NewSet)
- [NewSetFromSlice](#NewSetFromSlice)
- [New](#New)
- [FromSlice](#FromSlice)
- [Values](#Values)
- [Add](#Add)
- [AddIfNotExist](#AddIfNotExist)
@@ -41,20 +41,23 @@ import (
- [Intersection](#Intersection)
- [SymmetricDifference](#SymmetricDifference)
- [Minus](#Minus)
- [Pop](#Pop)
- [ToSlice](#ToSlice)
- [ToSortedSlice](#ToSortedSlice)
<div STYLE="page-break-after: always;"></div>
## Documentation
### <span id="NewSet">NewSet</span>
### <span id="New">New</span>
<p>Create a set instance</p>
<b>Signature:</b>
```go
type Set[T comparable] map[T]bool
func NewSet[T comparable](items ...T) Set[T]
type Set[T comparable] map[T]struct{}
func New[T comparable](items ...T) Set[T]
```
<b>Example:</b>
@@ -68,19 +71,19 @@ import (
)
func main() {
st := set.NewSet[int](1,2,2,3)
st := set.New[int](1,2,2,3)
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="NewSetFromSlice">NewSetFromSlice</span>
### <span id="FromSlice">FromSlice</span>
<p>Create a set from slice</p>
<b>Signature:</b>
```go
func NewSetFromSlice[T comparable](items []T) Set[T]
func FromSlice[T comparable](items []T) Set[T]
```
<b>Example:</b>
@@ -94,14 +97,15 @@ import (
)
func main() {
st := set.NewSetFromSlice([]int{1, 2, 2, 3})
st := set.FromSlice([]int{1, 2, 2, 3})
fmt.Println(st.Values()) //1,2,3
}
```
### <span id="Values">Values</span>
### <span id="Values">Values<sup>deprecated</sup></span>
<p>Return slice of all set data</p>
<p>Return slice of all set data.<br>
The <a href='#ToSlice'>ToSlice()</a> function provides the same functionality as Values and returns a slice containing all values of the set.</p>
<b>Signature:</b>
@@ -120,7 +124,7 @@ import (
)
func main() {
st := set.NewSet[int](1,2,2,3)
st := set.New[int](1,2,2,3)
fmt.Println(st.Values()) //1,2,3
}
```
@@ -146,7 +150,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
fmt.Println(st.Values()) //1,2,3
@@ -174,7 +178,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
r1 := st.AddIfNotExist(1)
@@ -207,7 +211,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2)
ok := st.AddIfNotExistBy(3, func(val int) bool {
@@ -246,7 +250,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
set.Delete(3)
@@ -275,7 +279,7 @@ import (
)
func main() {
st := set.NewSet[int]()
st := set.New[int]()
st.Add(1, 2, 3)
fmt.Println(st.Contain(1)) //true
@@ -304,9 +308,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(1, 2)
set3 := set.NewSet(1, 2, 3, 4)
set1 := set.New(1, 2, 3)
set2 := set.New(1, 2)
set3 := set.New(1, 2, 3, 4)
fmt.Println(set1.ContainAll(set2)) //true
fmt.Println(set1.ContainAll(set3)) //false
@@ -334,7 +338,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
fmt.Println(set1.Size()) //3
}
@@ -361,7 +365,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
set2 := set1.Clone()
fmt.Println(set1.Size() == set2.Size()) //true
@@ -390,9 +394,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(1, 2, 3)
set3 := set.NewSet(1, 2, 3, 4)
set1 := set.New(1, 2, 3)
set2 := set.New(1, 2, 3)
set3 := set.New(1, 2, 3, 4)
fmt.Println(set1.Equal(set2)) //true
fmt.Println(set1.Equal(set3)) //false
@@ -420,7 +424,7 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set1 := set.New(1, 2, 3)
arr := []int{}
set.Iterate(func(item int) {
arr = append(arr, item)
@@ -451,7 +455,7 @@ import (
)
func main() {
s := set.NewSet(1, 2, 3, 4, 5)
s := set.New(1, 2, 3, 4, 5)
var sum int
@@ -488,8 +492,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet()
set1 := set.New(1, 2, 3)
set2 := set.New()
fmt.Println(set1.IsEmpty()) //false
fmt.Println(set2.IsEmpty()) //true
@@ -517,8 +521,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.Union(set2)
fmt.Println(set3.Values()) //1,2,3,4,5
@@ -546,8 +550,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.Intersection(set2)
fmt.Println(set3.Values()) //2,3
@@ -575,8 +579,8 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set1.SymmetricDifference(set2)
fmt.Println(set3.Values()) //1,4,5
@@ -604,9 +608,9 @@ import (
)
func main() {
set1 := set.NewSet(1, 2, 3)
set2 := set.NewSet(2, 3, 4, 5)
set3 := set.NewSet(2, 3)
set1 := set.New(1, 2, 3)
set2 := set.New(2, 3, 4, 5)
set3 := set.New(2, 3)
res1 := set1.Minus(set2)
fmt.Println(res1.Values()) //1
@@ -637,7 +641,7 @@ import (
)
func main() {
s := set.NewSet[int]()
s := set.New[int]()
s.Add(1)
s.Add(2)
s.Add(3)
@@ -648,3 +652,58 @@ func main() {
fmt.Println(ok) // true
}
```
### <span id="ToSlice">ToSlice</span>
<p>returns a slice containing all values of the set.</p>
<b>Signature:</b>
```go
func (s Set[T]) ToSlice() (v T, ok bool)
```
<b>Example:</b>
```go
func main() {
s := set.New(1, 2, 3, 4, 5)
val := s.ToSlice()
fmt.Println(val) // [2 3 4 5 1]
}
```
### <span id="ToSortedSlice">ToSortedSlice</span>
<p>returns a sorted slice containing all values of the set</p>
<b>Signature:</b>
```go
func (s Set[T]) ToSortedSlice() (v T, ok bool)
```
<b>Example:</b>
```go
func main() {
s1 := set.New(1, 2, 3, 4, 5)
type Person struct {
Name string
Age int
}
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
return v1 < v2
})
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
return v1.Age < v2.Age
})
fmt.Println(res1) // [1 2 3 4 5]
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
}
```

View File

@@ -41,7 +41,7 @@ import (
## Documentation
## 1. BSTree
BSTree is a binary search tree data structure in which each node has at two children, which are referred to as the left child and the right child. In BSTree: leftNode < rootNode < rightNode. Type T should implements Compare function in lancetconstraints.Comparator interface.
BSTree is a binary search tree data structure in which each node has at two children, which are referred to as the left child and the right child. In BSTree: leftNode < rootNode < rightNode. Type T should implements Compare function in constraints.Comparator interface.
### <span id="NewBSTree">NewBSTree</span>
<p>Make a BSTree pointer instance</p>
@@ -49,11 +49,11 @@ BSTree is a binary search tree data structure in which each node has at two chil
<b>Signature:</b>
```go
func NewBSTree[T any](rootData T, comparator lancetconstraints.Comparator) *BSTree[T]
func NewBSTree[T any](rootData T, comparator constraints.Comparator) *BSTree[T]
type BSTree[T any] struct {
root *datastructure.TreeNode[T]
comparator lancetconstraints.Comparator
comparator constraints.Comparator
}
type TreeNode[T any] struct {

View File

@@ -26,6 +26,7 @@ import (
- [CreateFile](#CreateFile)
- [CreateDir](#CreateDir)
- [CopyFile](#CopyFile)
- [CopyDir](#CopyDir)
- [CurrentPath](#CurrentPath)
- [FileMode](#FileMode)
- [MiMeType](#MiMeType)
@@ -45,9 +46,12 @@ import (
- [Sha](#Sha)
- [ReadCsvFile](#ReadCsvFile)
- [WriteCsvFile](#WriteCsvFile)
- [WriteMapsToCsv](#WriteMapsToCsv)
- [WriteStringToFile](#WriteStringToFile)
- [WriteBytesToFile](#WriteBytesToFile)
- [ReadFile](#ReadFile)
- [ChunkRead](#ChunkRead)
- [ParallelChunkRead](#ParallelChunkRead)
<div STYLE="page-break-after: always;"></div>
@@ -161,6 +165,34 @@ func main() {
}
```
### <span id="CopyDir">CopyDir</span>
<p>copy src directory to dst directory, it will copy all files and directories recursively. the access permission will be the same as the source directory. if dstPath exists, it will return an error.</p>
<b>Signature:</b>
```go
func CopyDir(srcPath string, dstPath string) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/YAyFTA_UuPb)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
err := fileutil.CopyFile("./test_src", "./test_dest")
if err != nil {
fmt.Println(err)
}
}
```
### <span id="CurrentPath">CurrentPath</span>
<p>return current absolute path.</p>
@@ -669,7 +701,7 @@ func main() {
<b>Signature:</b>
```go
func ReadCsvFile(filepath string) ([][]string, error)
func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/OExTkhGEd3_u)</span></b>
@@ -701,7 +733,7 @@ func main() {
<b>Signature:</b>
```go
func WriteCsvFile(filepath string, records [][]string, append bool) error
func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/dAXm58Q5U1o)</span></b>
@@ -743,6 +775,59 @@ func main() {
}
```
### <span id="WriteMapsToCsv">WriteMapsToCsv</span>
<p>Write slice of map to csv file.</p>
<b>Signature:</b>
```go
// filepath: path of the CSV file.
// records: slice of maps to be written. the value of map should be basic type. The maps will be sorted by key in alphabeta order, then be written into csv file.
// appendToExistingFile: If true, data will be appended to the file if it exists.
// delimiter: Delimiter to use in the CSV file.
// headers: order of the csv column headers, needs to be consistent with the key of the map.
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error
```
<b>Example:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/umAIomZFV1c)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
fpath := "./test.csv"
fileutil.CreateFile(fpath)
f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777)
defer f.Close()
records := []map[string]any{
{"Name": "Lili", "Age": "22", "Gender": "female"},
{"Name": "Jim", "Age": "21", "Gender": "male"},
}
headers := []string{"Name", "Age", "Gender"}
err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers)
if err != nil {
log.Fatal(err)
}
content, err := fileutil.ReadCsvFile(csvFilePath, ';')
fmt.Println(content)
// Output:
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
}
```
### <span id="WriteBytesToFile">WriteBytesToFile</span>
<p>Writes bytes to target file.</p>
@@ -843,7 +928,7 @@ func main() {
### <span id="ReadFile">ReadFile</span>
<p>Read File/URL</p>
<p>Read File or URL.</p>
<b>Signature:</b>
@@ -851,7 +936,7 @@ func main() {
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error)
```
<b>Example:<span style="float:right;display:inline-block;"> </span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/uNep3Tr8fqF)</span></b>
```go
package main
@@ -862,19 +947,131 @@ import (
)
func main() {
reader, fn, err := ReadFile("https://httpbin.org/robots.txt")
if err != nil {
return
}
defer fn()
reader, fn, err := fileutil.ReadFile("https://httpbin.org/robots.txt")
if err != nil {
return
}
defer fn()
dat, err := io.ReadAll(reader)
if err != nil {
return
}
fmt.Println(string(dat))
// Output:
// User-agent: *
// Disallow: /deny
dat, err := io.ReadAll(reader)
if err != nil {
return
}
fmt.Println(string(dat))
// Output:
// User-agent: *
// 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
}
```

View File

@@ -7,6 +7,7 @@ Package function can control the flow of function execution and support part of
## Source:
- [https://github.com/duke-git/lancet/blob/main/function/function.go](https://github.com/duke-git/lancet/blob/main/function/function.go)
- [https://github.com/duke-git/lancet/blob/main/function/predicate.go](https://github.com/duke-git/lancet/blob/main/function/predicate.go)
- [https://github.com/duke-git/lancet/blob/main/function/watcher.go](https://github.com/duke-git/lancet/blob/main/function/watcher.go)
<div STYLE="page-break-after: always;"></div>
@@ -32,6 +33,13 @@ import (
- [Schedule](#Schedule)
- [Pipeline](#Pipeline)
- [Watcher](#Watcher)
- [And](#And)
- [Or](#Or)
- [Negate](#Negate)
- [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand)
- [AcceptIf](#AcceptIf)
<div STYLE="page-break-after: always;"></div>
@@ -403,3 +411,285 @@ func longRunningTask() {
}
```
### <span id="And">And</span>
<p>Returns a composed predicate that represents the logical AND of a list of predicates. It evaluates to true only if all predicates evaluate to true for the given value.</p>
<b>Signature:</b>
```go
func And[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isNumericAndLength5 := function.And(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcde"))
// Output:
// true
// false
// false
}
```
### <span id="Or">Or</span>
<p>Returns a composed predicate that represents the logical OR of a list of predicates. It evaluates to true if at least one of the predicates evaluates to true for the given value.</p>
<b>Signature:</b>
```go
func Or[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
containsDigitOrSpecialChar := function.Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
)
fmt.Println(containsDigitOrSpecialChar("hello!"))
fmt.Println(containsDigitOrSpecialChar("hello"))
// Output:
// true
// false
}
```
### <span id="Negate">Negate</span>
<p>Returns a predicate that represents the logical negation of this predicate.</p>
<b>Signature:</b>
```go
func Negate[T any](predicate func(T) bool) func(T) bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
// Define some simple predicates for demonstration
isUpperCase := func(s string) bool {
return strings.ToUpper(s) == s
}
isLowerCase := func(s string) bool {
return strings.ToLower(s) == s
}
isMixedCase := function.Negate(function.Or(isUpperCase, isLowerCase))
fmt.Println(isMixedCase("ABC"))
fmt.Println(isMixedCase("AbC"))
// Output:
// false
// true
}
```
### <span id="Nor">Nor</span>
<p>Returns a composed predicate that represents the logical NOR of a list of predicates. It evaluates to true only if all predicates evaluate to false for the given value.</p>
<b>Signature:</b>
```go
func Nor[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
match := function.Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("dbcdckkeee"))
match = function.Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("0123456789"))
// Output:
// true
// false
}
```
### <span id="Nand">Nand</span>
<p>Returns a composed predicate that represents the logical NAND of a list of predicates. It evaluates to true only if all predicates evaluate to false for the given value.</p>
<b>Signature:</b>
```go
func Nand[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isNumericAndLength5 := function.Nand(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcdef"))
// Output:
// false
// false
// true
}
```
### <span id="Xnor">Xnor</span>
<p>Returns a composed predicate that represents the logical XNOR of a list of predicates. It evaluates to true only if all predicates evaluate to true or false for the given value.</p>
<b>Signature:</b>
```go
func Xnor[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
isEven := func(i int) bool { return i%2 == 0 }
isPositive := func(i int) bool { return i > 0 }
match := function.Xnor(isEven, isPositive)
fmt.Println(match(2))
fmt.Println(match(-3))
fmt.Println(match(3))
// Output:
// true
// true
// false
}
```
### <span id="AcceptIf">AcceptIf</span>
<p>AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
A predicate function that takes an argument of type T and returns a bool.
An apply function that also takes an argument of type T and returns a modified value of the same type.</p>
<b>Signature:</b>
```go
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
```
<b>Example:</b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
adder := AcceptIf(
And(
func(x int) bool {
return x > 10
}, func(x int) bool {
return x%2 == 0
}),
func(x int) int {
return x + 1
},
)
result, ok := adder(20)
fmt.Println(result)
fmt.Println(ok)
result, ok = adder(21)
fmt.Println(result)
fmt.Println(ok)
// Output:
// 21
// true
// 0
// false
}
```

View File

@@ -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
}
```

View File

@@ -29,8 +29,11 @@ import (
- [RandLower](#RandLower)
- [RandNumeral](#RandNumeral)
- [RandNumeralOrLetter](#RandNumeralOrLetter)
- [RandSymbolChar](#RandSymbolChar)
- [UUIdV4](#UUIdV4)
- [RandUniqueIntSlice](#RandUniqueIntSlice)
- [RandFloat](#RandFloat)
- [RandFloats](#RandFloats)
<div STYLE="page-break-after: always;"></div>
@@ -218,6 +221,32 @@ func main() {
}
```
### <span id="RandSymbolChar">RandSymbolChar</span>
<p>Generate a random symbol char of specified length.</p>
<b>Signature:</b>
```go
func RandSymbolChar(length int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Im6ZJxAykOm)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
randStr := random.RandSymbolChar(6)
fmt.Println(randStr) // random string like: @#(_")
}
```
### <span id="UUIdV4">UUIdV4</span>
<p>Generate a random UUID of version 4 according to RFC 4122.</p>
@@ -273,3 +302,55 @@ func main() {
fmt.Println(result) //[0 4 7 1 5] (random)
}
```
### <span id="RandFloat">RandFloat</span>
<p>Generate a random float64 number between [min, max) with specific precision.</p>
<b>Signature:</b>
```go
func RandFloat(min, max float64, precision int) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/zbD_tuobJtr)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
floatNumber := random.RandFloat(1.0, 5.0, 2)
fmt.Println(floatNumber) //2.14 (random number)
}
```
### <span id="RandFloats">RandFloats</span>
<p>Generate a slice of random float64 numbers of length n that do not repeat.</p>
<b>Signature:</b>
```go
func RandFloats(n int, min, max float64, precision int) []float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/I3yndUQ-rhh)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
floatNumbers := random.RandFloats(5, 1.0, 5.0, 2)
fmt.Println(floatNumbers) //[3.42 3.99 1.3 2.38 4.23] (random)
}
```

View File

@@ -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
}
```

View File

@@ -35,6 +35,7 @@ import (
- [DifferenceBy](#DifferenceBy)
- [DifferenceWith](#DifferenceWith)
- [DeleteAt](#DeleteAt)
- [DeleteRange](#DeleteRange)
- [Drop](#Drop)
- [DropRight](#DropRight)
- [DropWhile](#DropWhile)
@@ -515,15 +516,15 @@ func main() {
### <span id="DeleteAt">DeleteAt</span>
<p>Delete the element of slice from start index to end index - 1.</p>
<p>Delete delete the element of slice at index.</p>
<b>Signature:</b>
```go
func DeleteAt[T any](slice []T, start int, end ...int)
func DeleteAt[T any](slice []T, index int)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/pJ-d6MUWcvK)</span></b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/800B1dPBYyd)</span></b>
```go
import (
@@ -532,18 +533,66 @@ import (
)
func main() {
result1 := slice.DeleteAt([]string{"a", "b", "c"}, -1)
result2 := slice.DeleteAt([]string{"a", "b", "c"}, 0)
result3 := slice.DeleteAt([]string{"a", "b", "c"}, 0, 2)
chars := []string{"a", "b", "c", "d", "e"}
result1 := slice.DeleteAt(chars, 0)
result2 := slice.DeleteAt(chars, 4)
result3 := slice.DeleteAt(chars, 5)
result4 := slice.DeleteAt(chars, 6)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
// Output:
// [a b c]
// [b c]
// [c]
// [b c d e]
// [a b c d]
// [a b c d]
// [a b c d]
}
```
### <span id="DeleteRange">DeleteRange</span>
<p>Delete the element of slice from start index to end indexexclude)</p>
<b>Signature:</b>
```go
func DeleteRange[T any](slice []T, start, end int) []T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/945HwiNrnle)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
chars := []string{"a", "b", "c", "d", "e"}
result1 := slice.DeleteRange(chars, 0, 0)
result2 := slice.DeleteRange(chars, 0, 1)
result3 := slice.DeleteRange(chars, 0, 3)
result4 := slice.DeleteRange(chars, 0, 4)
result5 := slice.DeleteRange(chars, 0, 5)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result4)
fmt.Println(result5)
// Output:
// [a b c d e]
// [b c d e]
// [d e]
// [e]
// []
}
```
@@ -2473,17 +2522,78 @@ import (
func main() {
nums := []int{1, 2, 3, 4, 5}
result1 := slice.Partition(nums)
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
result1 := slice.Partition(nums)
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// [[1 2 3 4 5]]
// [[2 4] [1 3 5]]
// [[1 2] [3 4] [5]]
// Output:
// [[1 2 3 4 5]]
// [[2 4] [1 3 5]]
// [[1 2] [3 4] [5]]
}
```
### <span id="Random">Random</span>
<p>Random get a random item of slice, return idx=-1 when slice is empty. </p>
<b>Signature:</b>
```go
func Random[T any](slice []T) (val T, idx int)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/UzpGQptWppw)</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
val, idx := slice.Random(nums)
if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) {
fmt.Println("okk")
}
// Output:
// okk
}
```
### <span id="SetToDefaultIf">SetToDefaultIf</span>
<p>Sets elements to their default value if they match the given predicate. It retains the positions of the elements in the slice. It returns slice of T and the count of modified slice items</p>
<b>Signature:</b>
```go
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
```
<b>Example:</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
}
```

View File

@@ -37,6 +37,7 @@ import (
- [IsExported](#IsExported)
- [IsZero](#IsZero)
- [IsSlice](#IsSlice)
- [IsTargetType](#IsTargetType)
<div STYLE="page-break-after: always;"></div>
@@ -533,3 +534,44 @@ func main() {
// true
}
```
### <span id="IsTargetType">IsTargetType</span>
<p>check if a struct field type is target type or not</p>
<b>Signature:</b>
```go
func (f *Field) IsTargetType(targetType reflect.Kind) bool
```
<b>Example:</b>
```go
package main
import (
"fmt"
"reflect"
"github.com/duke-git/lancet/v2/structs"
)
func main() {
type Parent struct {
Name string
arr []int
}
p1 := &Parent{arr: []int{1, 2, 3}}
s := structs.New(p1)
n, _ := s.Field("Name")
a, _ := s.Field("arr")
fmt.Println(n.IsTargetType(reflect.String))
fmt.Println(a.IsTargetType(reflect.Slice))
// Output:
// true
// true
}
```

View File

@@ -60,6 +60,8 @@ import (
- [ContainsAll](#ContainsAll)
- [ContainsAny](#ContainsAny)
- [RemoveWhiteSpace](#RemoveWhiteSpace)
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
<div STYLE="page-break-after: always;"></div>
@@ -1087,7 +1089,7 @@ func main() {
func IsNotBlank(str string) bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/e_oJW0RAquA)</span></b>
```go
import (
@@ -1096,11 +1098,11 @@ import (
)
func main() {
result1 := IsNotBlank("")
result2 := IsNotBlank(" ")
result3 := IsNotBlank("\t\v\f\n")
result4 := IsNotBlank(" 中文")
result5 := IsNotBlank(" world ")
result1 := strutil.IsNotBlank("")
result2 := strutil.IsNotBlank(" ")
result3 := strutil.IsNotBlank("\t\v\f\n")
result4 := strutil.IsNotBlank(" 中文")
result5 := strutil.IsNotBlank(" world ")
fmt.Println(result1)
fmt.Println(result2)
@@ -1463,3 +1465,69 @@ func main() {
// hello world
}
```
### <span id="SubInBetween">SubInBetween</span>
<p>Return substring between the start and end position(excluded) of source string.</p>
<b>Signature:</b>
```go
func SubInBetween(str string, start string, end string) string
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
str := "abcde"
result1 := strutil.SubInBetween(str, "", "de")
result2 := strutil.SubInBetween(str, "a", "d")
fmt.Println(result1)
fmt.Println(result2)
// Output:
// abc
// bc
}
```
### <span id="HammingDistance">HammingDistance</span>
<p>HammingDistance calculates the Hamming distance between two strings. The Hamming distance is the number of positions at which the corresponding symbols are different.</p>
<b>Signature:</b>
```go
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
}
```

View File

@@ -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.

View File

@@ -30,7 +30,7 @@ Lancet 的成长离不开大家的支持,如果你愿意为 Lancet 贡献代
- 提交 PR 前请执行单元测试命令go test -v ./...,确保所有单元测试任务通过。
- 确保 PR 是提交到 `v2` 分支,而不是 `main` 分支。
- 确保 PR 是提交到 `rc` 分支,而不是其他分支。
- 如果是修复 bug请在 PR 中给出描述信息。

View File

@@ -20,11 +20,68 @@ import (
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"
"github.com/duke-git/lancet/v2/validator"
)
// FileReader is a reader supporting offset seeking and reading one
// line at a time, this is especially useful for large files
type FileReader struct {
*bufio.Reader
file *os.File
offset int64
}
// NewFileReader creates the FileReader struct for reading
func NewFileReader(path string) (*FileReader, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
return &FileReader{
file: f,
Reader: bufio.NewReader(f),
offset: 0,
}, nil
}
// ReadLine reads and returns one line at a time excluding the trailing '\r' and '\n'
func (f *FileReader) ReadLine() (string, error) {
data, err := f.Reader.ReadBytes('\n')
f.offset += int64(len(data))
if err == nil || err == io.EOF {
for len(data) > 0 && (data[len(data)-1] == '\r' || data[len(data)-1] == '\n') {
data = data[:len(data)-1]
}
return string(data), err
}
return "", err
}
// Offset returns the current offset of the file
func (f *FileReader) Offset() int64 {
return f.offset
}
// SeekOffset sets the current offset of the reading
func (f *FileReader) SeekOffset(offset int64) error {
_, err := f.file.Seek(offset, 0)
if err != nil {
return err
}
f.Reader = bufio.NewReader(f.file)
f.offset = offset
return nil
}
// Close takes care of the opened file
func (f *FileReader) Close() error {
return f.file.Close()
}
// IsExist checks if a file or directory exists.
// Play: https://go.dev/play/p/nKKXt8ZQbmh
func IsExist(path string) bool {
@@ -57,6 +114,65 @@ func CreateDir(absPath string) error {
return os.MkdirAll(absPath, os.ModePerm)
}
// CopyDir copy src directory to dst directory, it will copy all files and directories recursively.
// the access permission will be the same as the source directory.
// if dstPath exists, it will return an error.
// Play: https://go.dev/play/p/YAyFTA_UuPb
func CopyDir(srcPath string, dstPath string) error {
if !IsDir(srcPath) {
return errors.New("source path is not a directory")
}
var err error
srcPath, err = filepath.Abs(srcPath)
if err != nil {
return err
}
if IsExist(dstPath) {
return errors.New("destination path already exists")
}
dstPath, err = filepath.Abs(dstPath)
if err != nil {
return err
}
// get srcPath's file info
srcFileInfo, err := os.Stat(srcPath)
if err != nil {
return err
}
// create dstPath with srcPath's mode
err = os.MkdirAll(dstPath, srcFileInfo.Mode())
if err != nil {
return err
}
err = filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error {
if srcPath == path {
return nil
}
curDstPath := filepath.Join(dstPath, filepath.Base(path))
if info.IsDir() {
err = CopyDir(path, curDstPath)
if err != nil {
return err
}
} else {
err = CopyFile(path, curDstPath)
if err != nil {
return err
}
err = os.Chmod(curDstPath, info.Mode())
if err != nil {
return err
}
}
return err
})
return err
}
// IsDir checks if the path is directory or not.
// Play: https://go.dev/play/p/WkVwEKqtOWk
func IsDir(path string) bool {
@@ -321,7 +437,7 @@ func UnZip(zipFile string, destPath string) error {
defer zipReader.Close()
for _, f := range zipReader.File {
//issue#62: fix ZipSlip bug
// issue#62: fix ZipSlip bug
path, err := safeFilepathJoin(destPath, f.Name)
if err != nil {
return err
@@ -508,6 +624,25 @@ func FileSize(path string) (int64, error) {
return f.Size(), nil
}
// DirSize walks the folder recursively and returns folder size in bytes.
func DirSize(path string) (int64, error) {
var size int64
err := filepath.WalkDir(path, func(_ string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
info, err := d.Info()
if err != nil {
return err
}
size += info.Size()
}
return err
})
return size, err
}
// MTime returns file modified time.
// Play: https://go.dev/play/p/s_Tl7lZoAaY
func MTime(filepath string) (int64, error) {
@@ -536,7 +671,7 @@ func Sha(filepath string, shaType ...int) (string, error) {
} else if shaType[0] == 512 {
h = sha512.New()
} else {
return "", errors.New("param `shaType` should be 1, 256 or 512.")
return "", errors.New("param `shaType` should be 1, 256 or 512")
}
}
@@ -554,15 +689,19 @@ func Sha(filepath string, shaType ...int) (string, error) {
// ReadCsvFile read file content into slice.
// Play: https://go.dev/play/p/OExTkhGEd3_u
func ReadCsvFile(filepath string) ([][]string, error) {
func ReadCsvFile(filepath string, delimiter ...rune) ([][]string, error) {
f, err := os.Open(filepath)
if err != nil {
return nil, err
}
defer f.Close()
csvReader := csv.NewReader(f)
records, err := csvReader.ReadAll()
reader := csv.NewReader(f)
if len(delimiter) > 0 {
reader.Comma = delimiter[0]
}
records, err := reader.ReadAll()
if err != nil {
return nil, err
}
@@ -571,8 +710,10 @@ func ReadCsvFile(filepath string) ([][]string, error) {
}
// WriteCsvFile write content to target csv file.
// append: append to existing csv file
// delimiter: specifies csv delimiter
// Play: https://go.dev/play/p/dAXm58Q5U1o
func WriteCsvFile(filepath string, records [][]string, append bool) error {
func WriteCsvFile(filepath string, records [][]string, append bool, delimiter ...rune) error {
flag := os.O_RDWR | os.O_CREATE
if append {
@@ -587,7 +728,19 @@ func WriteCsvFile(filepath string, records [][]string, append bool) error {
defer f.Close()
writer := csv.NewWriter(f)
writer.Comma = ','
// 设置默认分隔符为逗号,除非另外指定
if len(delimiter) > 0 {
writer.Comma = delimiter[0]
} else {
writer.Comma = ','
}
// 遍历所有记录并处理包含分隔符或双引号的单元格
for i := range records {
for j := range records[i] {
records[i][j] = escapeCSVField(records[i][j], writer.Comma)
}
}
return writer.WriteAll(records)
}
@@ -627,7 +780,7 @@ func WriteBytesToFile(filepath string, content []byte) error {
}
// ReadFile get file reader by a url or a local file
// Play: todo
// Play: https://go.dev/play/p/uNep3Tr8fqF
func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error) {
switch {
case validator.IsUrl(path):
@@ -646,3 +799,166 @@ func ReadFile(path string) (reader io.ReadCloser, closeFn func(), err error) {
return nil, func() {}, errors.New("unknown file type")
}
}
// escapeCSVField 处理单元格内容,如果包含分隔符,则用双引号包裹
func escapeCSVField(field string, delimiter rune) string {
// 替换所有的双引号为两个双引号
escapedField := strings.ReplaceAll(field, "\"", "\"\"")
// 如果字段包含分隔符、双引号或换行符,用双引号包裹整个字段
if strings.ContainsAny(escapedField, string(delimiter)+"\"\n") {
escapedField = fmt.Sprintf("\"%s\"", escapedField)
}
return escapedField
}
// WriteMapsToCsv write slice of map to csv file.
// Play: https://go.dev/play/p/umAIomZFV1c
// filepath: Path to the CSV file.
// records: Slice of maps to be written. the value of map should be basic type.
// the maps will be sorted by key in alphabeta order, then be written into csv file.
// appendToExistingFile: If true, data will be appended to the file if it exists.
// delimiter: Delimiter to use in the CSV file.
// headers: order of the csv column headers, needs to be consistent with the key of the map.
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune,
headers ...[]string) error {
for _, record := range records {
for _, value := range record {
if !isCsvSupportedType(value) {
return errors.New("unsupported value type detected; only basic types are supported: \nbool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr")
}
}
}
var columnHeaders []string
if len(headers) > 0 {
columnHeaders = headers[0]
} else {
for key := range records[0] {
columnHeaders = append(columnHeaders, key)
}
// sort keys in alphabeta order
sort.Strings(columnHeaders)
}
var datasToWrite [][]string
if !appendToExistingFile {
datasToWrite = append(datasToWrite, columnHeaders)
}
for _, record := range records {
var row []string
for _, h := range columnHeaders {
row = append(row, fmt.Sprintf("%v", record[h]))
}
datasToWrite = append(datasToWrite, row)
}
return WriteCsvFile(filepath, datasToWrite, appendToExistingFile, delimiter)
}
// check if the value of map which to be written into csv is basic type.
func isCsvSupportedType(v interface{}) bool {
switch v.(type) {
case bool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr:
return true
default:
return false
}
}
// ChunkRead reads a block from the file at the specified offset and returns all lines within the block
// Play: 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
}

View File

@@ -3,7 +3,10 @@ package fileutil
import (
"fmt"
"io"
"log"
"os"
"runtime"
"sync"
)
func ExampleIsExist() {
@@ -331,6 +334,28 @@ func ExampleWriteCsvFile() {
// [[Lili 22 female] [Jim 21 male]]
}
func ExampleWriteMapsToCsv() {
csvFilePath := "./testdata/test3.csv"
records := []map[string]any{
{"Name": "Lili", "Age": "22", "Gender": "female"},
{"Name": "Jim", "Age": "21", "Gender": "male"},
}
headers := []string{"Name", "Age", "Gender"}
err := WriteMapsToCsv(csvFilePath, records, false, ';', headers)
if err != nil {
log.Fatal(err)
}
content, err := ReadCsvFile(csvFilePath, ';')
fmt.Println(content)
// Output:
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
}
func ExampleWriteStringToFile() {
filepath := "./test.txt"
@@ -398,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
}

View File

@@ -3,6 +3,10 @@ package fileutil
import (
"io"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"testing"
"github.com/duke-git/lancet/v2/internal"
@@ -27,7 +31,7 @@ func TestCreateFile(t *testing.T) {
assert := internal.NewAssert(t, "TestCreateFile")
f := "./text.txt"
f := "./testdata/text.txt"
if CreateFile(f) {
file, err := os.Open(f)
assert.IsNil(err)
@@ -340,10 +344,14 @@ func TestSha(t *testing.T) {
assert := internal.NewAssert(t, "TestSha")
sha1, err := Sha("./testdata/test.txt", 1)
sha256, err := Sha("./testdata/test.txt", 256)
sha512, err := Sha("./testdata/test.txt", 512)
assert.IsNil(err)
sha256, err := Sha("./testdata/test.txt", 256)
assert.IsNil(err)
sha512, err := Sha("./testdata/test.txt", 512)
assert.IsNil(err)
assert.Equal("dda3cf10c5a6ff6c6659a497bf7261b287af2bc7", sha1)
assert.Equal("aa6d0a3fbc3442c228d606da09e0c1dc98c69a1cac3da1909199e0266171df35", sha256)
assert.Equal("d22aba2a1b7a2e2f512756255cc1c3708905646920cb1eb95e45b531ba74774dbbb89baebf1f716220eb9cf4908f1cfc5b2a01267704d9a59f59d77cab609870", sha512)
@@ -379,11 +387,36 @@ func TestWriteCsvFile(t *testing.T) {
assert.Equal(2, len(readContent))
assert.Equal(3, len(readContent[0]))
assert.Equal("Lili", content[0][0])
assert.Equal("Lili", readContent[0][0])
// RemoveFile(csvFilePath)
}
func TestWriteMapsToCsv(t *testing.T) {
assert := internal.NewAssert(t, "TestWriteMapsToCSV")
csvFilePath := "./testdata/test4.csv"
records := []map[string]any{
{"Name": "Lili", "Age": "22", "Gender": "female"},
{"Name": "Jim", "Age": "21", "Gender": "male"},
}
headers := []string{"Name", "Age", "Gender"}
err := WriteMapsToCsv(csvFilePath, records, false, ';', headers)
assert.IsNil(err)
content, err := ReadCsvFile(csvFilePath, ';')
assert.IsNil(err)
assert.Equal(3, len(content))
assert.Equal(3, len(content[0]))
assert.Equal("Lili", content[1][0])
assert.Equal("22", content[1][1])
assert.Equal("female", content[1][2])
}
func TestWriteStringToFile(t *testing.T) {
assert := internal.NewAssert(t, "TestWriteStringToFile")
@@ -476,3 +509,113 @@ Disallow: /deny
`
internal.NewAssert(t, "TestReadFile").Equal(want, string(dat))
}
func TestReadlineFile(t *testing.T) {
path := "./testdata/demo.csv"
reader, err := NewFileReader(path)
if err != nil {
t.Fail()
}
defer reader.Close()
indexMap := make(map[string]int64)
defer reader.Close()
for {
offset := reader.Offset()
line, err := reader.ReadLine()
if err == io.EOF {
break
}
indexMap[line] = offset
}
lines, err := ReadFileByLine(path)
if err != nil {
t.Fail()
}
for _, line := range lines {
offset, ok := indexMap[line]
if !ok {
t.Fail()
}
if err = reader.SeekOffset(offset); err != nil {
t.Fail()
}
lineRead, err := reader.ReadLine()
if err == io.EOF {
break
}
internal.NewAssert(t, "TestReadlineFile").Equal(line, lineRead)
}
}
func TestCopyDir(t *testing.T) {
assert := internal.NewAssert(t, "TestCopyDir")
src := "./testdata"
dest := "./testdata_copy"
err := CopyDir(src, dest)
assert.IsNil(err)
assert.Equal(true, IsExist(dest))
filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
destPath := strings.Replace(path, src, dest, 1)
assert.Equal(true, IsExist(destPath))
return nil
})
os.RemoveAll(dest)
}
func TestParallelChunkRead(t *testing.T) {
assert := internal.NewAssert(t, "TestParallelChunkRead")
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
filePath := "./testdata/test1.csv" // 替换为你的文件路径
go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
var totalLines int
for lines := range linesCh {
totalLines += len(lines)
assert.Equal("Lili,22,female", lines[0])
assert.Equal("Jim,21,male", lines[1])
}
assert.Equal(2, totalLines)
}
func TestChunkRead(t *testing.T) {
assert := internal.NewAssert(t, "TestChunkRead")
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
filePath := "./testdata/test1.csv" // 替换为你的文件路径
f, err := os.Open(filePath)
if err != nil {
return
}
defer f.Close()
var bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultChunkSizeMB*mb)
},
}
lines, err := ChunkRead(f, 0, 100, &bufPool)
assert.Equal("Lili,22,female", lines[0])
assert.Equal("Jim,21,male", lines[1])
}

1
fileutil/testdata/test01/demo2.csv vendored Normal file
View File

@@ -0,0 +1 @@
makj1
1 makj1

View File

@@ -1,3 +1,2 @@
Lili,22,female
Jim,21,male
1 Lili 22 female
2 Jim 21 male

View File

@@ -1,3 +1,5 @@
Lili,22,female
Jim,21,male
1 Lili 22 female
2 Jim 21 male
3
4
5

3
fileutil/testdata/test3.csv vendored Normal file
View File

@@ -0,0 +1,3 @@
Name;Age;Gender
Lili;22;female
Jim;21;male
1 Name Age Gender
2 Lili 22 female
3 Jim 21 male

3
fileutil/testdata/test4.csv vendored Normal file
View File

@@ -0,0 +1,3 @@
Name;Age;Gender
Lili;22;female
Jim;21;male
1 Name Age Gender
2 Lili 22 female
3 Jim 21 male

View File

@@ -6,12 +6,11 @@ package formatter
import (
"encoding/json"
"fmt"
"io"
"strconv"
"strings"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/strutil"
"github.com/duke-git/lancet/v2/validator"
"golang.org/x/exp/constraints"
)
@@ -20,31 +19,24 @@ import (
// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345"
// Play: https://go.dev/play/p/eRD5k2vzUVX
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string {
if validator.IsInt(value) {
v, err := convertor.ToInt(value)
if err != nil {
return ""
}
return symbol + commaInt(v)
}
numString := convertor.ToString(value)
if validator.IsFloat(value) {
v, err := convertor.ToFloat(value)
if err != nil {
return ""
}
return symbol + commaFloat(v)
}
if strutil.IsString(value) {
v := fmt.Sprintf("%v", value)
if validator.IsNumberStr(v) {
return symbol + commaStr(v)
}
_, err := strconv.ParseFloat(numString, 64)
if err != nil {
return ""
}
return ""
index := strings.Index(numString, ".")
if index == -1 {
index = len(numString)
}
for index > 3 {
index = index - 3
numString = numString[:index] + "," + numString[index:]
}
return symbol + numString
}
// Pretty data to JSON string.

View File

@@ -1,85 +0,0 @@
package formatter
import (
"bytes"
"math"
"strconv"
"strings"
)
// see https://github.com/dustin/go-humanize/blob/master/comma.go
func commaInt(v int64) string {
sign := ""
// Min int64 can't be negated to a usable value, so it has to be special cased.
if v == math.MinInt64 {
return "-9,223,372,036,854,775,808"
}
if v < 0 {
sign = "-"
v = 0 - v
}
parts := []string{"", "", "", "", "", "", ""}
j := len(parts) - 1
for v > 999 {
parts[j] = strconv.FormatInt(v%1000, 10)
switch len(parts[j]) {
case 2:
parts[j] = "0" + parts[j]
case 1:
parts[j] = "00" + parts[j]
}
v = v / 1000
j--
}
parts[j] = strconv.Itoa(int(v))
return sign + strings.Join(parts[j:], ",")
}
func commaFloat(v float64) string {
buf := &bytes.Buffer{}
if v < 0 {
buf.Write([]byte{'-'})
v = 0 - v
}
comma := []byte{','}
parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".")
pos := 0
if len(parts[0])%3 != 0 {
pos += len(parts[0]) % 3
buf.WriteString(parts[0][:pos])
buf.Write(comma)
}
for ; pos < len(parts[0]); pos += 3 {
buf.WriteString(parts[0][pos : pos+3])
buf.Write(comma)
}
buf.Truncate(buf.Len() - 1)
if len(parts) > 1 {
buf.Write([]byte{'.'})
buf.WriteString(parts[1])
}
return buf.String()
}
func commaStr(s string) string {
dotIndex := strings.Index(s, ".")
if dotIndex != -1 {
return commaStrRecursive(s[:dotIndex]) + s[dotIndex:]
}
return commaStrRecursive(s)
}
func commaStrRecursive(s string) string {
if len(s) <= 3 {
return s
}
return commaStrRecursive(s[:len(s)-3]) + "," + commaStrRecursive(s[len(s)-3:])
}

View File

@@ -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))

View File

@@ -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
}

View File

@@ -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,
)
}

91
function/predicate.go Normal file
View File

@@ -0,0 +1,91 @@
package function
// And returns a composed predicate that represents the logical AND of a list of predicates.
// It evaluates to true only if all predicates evaluate to true for the given value.
func And[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if !predicate(value) {
return false // Short-circuit on the first false predicate
}
}
return true // True if all predicates are true
}
}
// Nand returns a composed predicate that represents the logical NAND of a list of predicates.
// It evaluates to true only if all predicates evaluate to false for the given value.
func Nand[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if predicate(value) {
return false // Short-circuit on the first true predicate
}
}
return true // True if all predicates are false
}
}
// Negate returns a predicate that represents the logical negation of this predicate.
func Negate[T any](predicate func(T) bool) func(T) bool {
return func(value T) bool {
return !predicate(value)
}
}
// Or returns a composed predicate that represents the logical OR of a list of predicates.
// It evaluates to true if at least one of the predicates evaluates to true for the given value.
func Or[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if predicate(value) {
return true // Short-circuit on the first true predicate
}
}
return false // False if all predicates are false
}
}
// Nor returns a composed predicate that represents the logical NOR of a list of predicates.
// It evaluates to true only if all predicates evaluate to false for the given value.
func Nor[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
for _, predicate := range predicates {
if predicate(value) {
return false // If any predicate evaluates to true, the NOR result is false
}
}
return true // Only returns true if all predicates evaluate to false
}
}
// Xnor returns a composed predicate that represents the logical XNOR of a list of predicates.
// It evaluates to true only if all predicates evaluate to true or false for the given value.
func Xnor[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
}
return func(value T) bool {
trueCount := 0
for _, predicate := range predicates {
if predicate(value) {
trueCount++
}
}
// XNOR is true if either all predicates are true or all are false
// This is the same as saying trueCount is 0 (all false) or trueCount is len(predicates) (all true)
return trueCount == 0 || trueCount == len(predicates)
}
}

View File

@@ -0,0 +1,128 @@
package function
import (
"fmt"
"strings"
)
func ExampleNegate() {
// Define some simple predicates for demonstration
isUpperCase := func(s string) bool {
return strings.ToUpper(s) == s
}
isLowerCase := func(s string) bool {
return strings.ToLower(s) == s
}
isMixedCase := Negate(Or(isUpperCase, isLowerCase))
fmt.Println(isMixedCase("ABC"))
fmt.Println(isMixedCase("AbC"))
// Output:
// false
// true
}
func ExampleOr() {
containsDigitOrSpecialChar := Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
)
fmt.Println(containsDigitOrSpecialChar("hello!"))
fmt.Println(containsDigitOrSpecialChar("hello"))
// Output:
// true
// false
}
func ExampleAnd() {
isNumericAndLength5 := And(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcde"))
// Output:
// true
// false
// false
}
func ExampleNand() {
isNumericAndLength5 := Nand(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(isNumericAndLength5("12345"))
fmt.Println(isNumericAndLength5("1234"))
fmt.Println(isNumericAndLength5("abcdef"))
// Output:
// false
// false
// true
}
func ExampleNor() {
match := Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("dbcdckkeee"))
match = Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
fmt.Println(match("0123456789"))
// Output:
// true
// false
}
func ExampleXnor() {
isEven := func(i int) bool { return i%2 == 0 }
isPositive := func(i int) bool { return i > 0 }
match := Xnor(isEven, isPositive)
fmt.Println(match(2))
fmt.Println(match(-3))
fmt.Println(match(3))
// Output:
// true
// true
// false
}
// func ExamplePredicatesMix() {
// a := Or(
// func(s string) bool { return strings.ContainsAny(s, "0123456789") },
// func(s string) bool { return strings.ContainsAny(s, "!") },
// )
// b := And(
// func(s string) bool { return strings.ContainsAny(s, "hello") },
// func(s string) bool { return strings.ContainsAny(s, "!") },
// )
// c := Negate(And(a, b))
// fmt.Println(c("hello!"))
// c = Nor(a, b)
// fmt.Println(c("hello!"))
// // Output:
// // false
// // false
// }

134
function/predicate_test.go Normal file
View File

@@ -0,0 +1,134 @@
package function
import (
"strings"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestPredicatesNegatePure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesNegatePure")
// Define some simple predicates for demonstration
isUpperCase := func(s string) bool {
return strings.ToUpper(s) == s
}
isLowerCase := func(s string) bool {
return strings.ToLower(s) == s
}
isMixedCase := Negate(Or(isUpperCase, isLowerCase))
assert.ShouldBeFalse(isMixedCase("ABC"))
assert.ShouldBeTrue(isMixedCase("AbC"))
}
func TestPredicatesOrPure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesOrPure")
containsDigitOrSpecialChar := Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!@#$%") },
)
assert.ShouldBeTrue(containsDigitOrSpecialChar("hello!"))
assert.ShouldBeFalse(containsDigitOrSpecialChar("hello"))
}
func TestPredicatesAndPure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesAndPure")
isNumericAndLength5 := And(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
assert.ShouldBeTrue(isNumericAndLength5("12345"))
assert.ShouldBeFalse(isNumericAndLength5("1234"))
assert.ShouldBeFalse(isNumericAndLength5("abcde"))
}
func TestPredicatesNandPure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesNandPure")
isNumericAndLength5 := Nand(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
assert.ShouldBeFalse(isNumericAndLength5("12345"))
assert.ShouldBeFalse(isNumericAndLength5("1234"))
assert.ShouldBeTrue(isNumericAndLength5("abcdef"))
}
func TestPredicatesNorPure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesNorPure")
match := Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
assert.ShouldBeTrue(match("dbcdckkeee"))
match = Nor(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return len(s) == 5 },
)
assert.ShouldBeFalse(match("0123456789"))
}
func TestPredicatesXnorPure(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesXnorPure")
isEven := func(i int) bool { return i%2 == 0 }
isPositive := func(i int) bool { return i > 0 }
match := Xnor(isEven, isPositive)
assert.ShouldBeTrue(match(2))
assert.ShouldBeTrue(match(-3))
assert.ShouldBeFalse(match(3))
}
func TestPredicatesMix(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestPredicatesMix")
a := Or(
func(s string) bool { return strings.ContainsAny(s, "0123456789") },
func(s string) bool { return strings.ContainsAny(s, "!") },
)
b := And(
func(s string) bool { return strings.ContainsAny(s, "hello") },
func(s string) bool { return strings.ContainsAny(s, "!") },
)
c := Negate(And(a, b))
assert.ShouldBeFalse(c("hello!"))
k := Nor(a, b)
assert.ShouldBeFalse(k("hello!"))
o := Xnor(a, b)
assert.ShouldBeTrue(o("hello!"))
p := Nand(c, k)
assert.ShouldBeTrue(p("hello!"))
}

View File

@@ -37,6 +37,20 @@ func (a *Assert) Equal(expected, actual any) {
}
}
// ShouldBeFalse check if expected is false
func (a *Assert) ShouldBeFalse(actual any) {
if compare(false, actual) != compareEqual {
makeTestFailed(a.T, a.CaseName, false, actual)
}
}
// ShouldBeTrue check if expected is true
func (a *Assert) ShouldBeTrue(actual any) {
if compare(true, actual) != compareEqual {
makeTestFailed(a.T, a.CaseName, true, actual)
}
}
// NotEqual check if expected is not equal with actual
func (a *Assert) NotEqual(expected, actual any) {
if compare(expected, actual) == compareEqual {

View File

@@ -6,10 +6,11 @@ import (
func TestAssert(t *testing.T) {
assert := NewAssert(t, "TestAssert")
assert.Equal(0, 0)
assert.NotEqual(1, 0)
assert.NotEqual("1", 1)
var uInt1 uint
var uInt2 uint
var uInt8 uint8
@@ -47,4 +48,11 @@ func TestAssert(t *testing.T) {
assert.IsNil(nil)
assert.IsNotNil("abc")
var valA int = 1
var valB int64 = 1
assert.NotEqual(valA, valB)
assert.EqualValues(valA, valB)
assert.ShouldBeFalse(false)
assert.ShouldBeTrue(true)
}

View File

@@ -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
}

View File

@@ -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))

View File

@@ -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)

View File

@@ -5,6 +5,7 @@
package maputil
import (
"fmt"
"reflect"
"github.com/duke-git/lancet/v2/slice"
@@ -303,3 +304,83 @@ func HasKey[K comparable, V any](m map[K]V, key K) bool {
_, haskey := m[key]
return haskey
}
// MapToStruct converts map to struct
// Play: todo
func MapToStruct(m map[string]any, structObj any) error {
for k, v := range m {
err := setStructField(structObj, k, v)
if err != nil {
return err
}
}
return nil
}
func setStructField(structObj any, fieldName string, fieldValue any) error {
structVal := reflect.ValueOf(structObj).Elem()
fName := getFieldNameByJsonTag(structObj, fieldName)
if fName == "" {
return fmt.Errorf("Struct field json tag don't match map key : %s in obj", fieldName)
}
fieldVal := structVal.FieldByName(fName)
if !fieldVal.IsValid() {
return fmt.Errorf("No such field: %s in obj", fieldName)
}
if !fieldVal.CanSet() {
return fmt.Errorf("Cannot set %s field value", fieldName)
}
val := reflect.ValueOf(fieldValue)
if fieldVal.Type() != val.Type() {
if val.CanConvert(fieldVal.Type()) {
fieldVal.Set(val.Convert(fieldVal.Type()))
return nil
}
if m, ok := fieldValue.(map[string]any); ok {
if fieldVal.Kind() == reflect.Struct {
return MapToStruct(m, fieldVal.Addr().Interface())
}
if fieldVal.Kind() == reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct {
if fieldVal.IsNil() {
fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
}
return MapToStruct(m, fieldVal.Interface())
}
}
return fmt.Errorf("Map value type don't match struct field type")
}
fieldVal.Set(val)
return nil
}
func getFieldNameByJsonTag(structObj any, jsonTag string) string {
s := reflect.TypeOf(structObj).Elem()
for i := 0; i < s.NumField(); i++ {
field := s.Field(i)
tag := field.Tag
name := tag.Get("json")
if name == jsonTag {
return field.Name
}
}
return ""
}

View File

@@ -472,3 +472,42 @@ func TestHasKey(t *testing.T) {
assert.Equal(true, HasKey(m, "a"))
assert.Equal(false, HasKey(m, "c"))
}
func TestMapToStruct(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestMapToStruct")
type (
Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Addr *Address `json:"address"`
}
Address struct {
Street string `json:"street"`
Number int `json:"number"`
}
)
m := map[string]interface{}{
"name": "Nothin",
"age": 28,
"phone": "123456789",
"address": map[string]interface{}{
"street": "test",
"number": 1,
},
}
var p Person
err := MapToStruct(m, &p)
assert.IsNil(err)
assert.Equal(m["name"], p.Name)
assert.Equal(m["age"], p.Age)
assert.Equal(m["phone"], p.Phone)
assert.Equal("test", p.Addr.Street)
assert.Equal(1, p.Addr.Number)
}

View File

@@ -62,18 +62,25 @@ var _ = func() struct{} {
*/
// Play: https://go.dev/play/p/4K7KBEPgS5M
func MapTo(src any, dst any) error {
dstRef := reflect.ValueOf(dst)
if dstRef.Kind() != reflect.Ptr {
return fmt.Errorf("dst is not ptr")
}
dstElem := dstRef.Type().Elem()
if dstElem.Kind() == reflect.Struct {
srcMap := src.(map[string]interface{})
return MapToStruct(srcMap, dst)
}
dstRef = reflect.Indirect(dstRef)
srcRef := reflect.ValueOf(src)
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
srcRef = srcRef.Elem()
}
if f, ok := mapHandlers[srcRef.Kind()]; ok {
return f(srcRef, dstRef)
}
@@ -121,7 +128,7 @@ func convertSlice(src reflect.Value, dst reflect.Value) error {
func convertMap(src reflect.Value, dst reflect.Value) error {
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
if src.Kind() == reflect.Interface {
if src.Kind() == reflect.Interface && dst.IsValid() {
return convertMap(src.Elem(), dst)
} else {
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
@@ -129,7 +136,9 @@ func convertMap(src reflect.Value, dst reflect.Value) error {
}
dstType := dst.Type()
num := dstType.NumField()
exist := map[string]int{}
for i := 0; i < num; i++ {
k := dstType.Field(i).Tag.Get("json")
if k == "" {
@@ -138,7 +147,6 @@ func convertMap(src reflect.Value, dst reflect.Value) error {
if strings.Contains(k, ",") {
taglist := strings.Split(k, ",")
if taglist[0] == "" {
k = dstType.Field(i).Name
} else {
k = taglist[0]
@@ -150,9 +158,11 @@ func convertMap(src reflect.Value, dst reflect.Value) error {
}
keys := src.MapKeys()
for _, key := range keys {
if index, ok := exist[key.String()]; ok {
v := dst.Field(index)
if v.Kind() == reflect.Struct {
err := convertMap(src.MapIndex(key), v)
if err != nil {

View File

@@ -8,10 +8,10 @@ import (
type (
Person struct {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Addr Address `json:"address"`
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Address *Address `json:"address"`
}
Address struct {
@@ -38,11 +38,12 @@ func TestStructType(t *testing.T) {
var p Person
err := MapTo(src, &p)
assert.IsNil(err)
assert.Equal(src["name"], p.Name)
assert.Equal(src["age"], p.Age)
assert.Equal(src["phone"], p.Phone)
assert.Equal("test", p.Addr.Street)
assert.Equal(1, p.Addr.Number)
assert.Equal("test", p.Address.Street)
assert.Equal(1, p.Address.Number)
}
func TestBaseType(t *testing.T) {

View File

@@ -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 {
@@ -330,10 +368,10 @@ func Cos(radian float64, precision ...int) float64 {
return TruncRound(radian, 3)
}
// Cos returns the sine of the radian argument.
// Sin returns the sine of the radian argument.
// Play: https://go.dev/play/p/TWMQlMywDsP
func Sin(radian float64, precision ...int) float64 {
return Cos((math.Pi / 2) - radian)
return Cos((math.Pi/2)-radian, precision...)
}
// Log returns the logarithm of base n.
@@ -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)
}

View File

@@ -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
}

View File

@@ -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)))
}

View File

@@ -14,6 +14,7 @@ package netutil
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
@@ -110,22 +111,23 @@ type HttpClientConfig struct {
Verbose bool
}
// defaultHttpClientConfig defalut client config
// defaultHttpClientConfig defalut client config.
var defaultHttpClientConfig = &HttpClientConfig{
Compressed: false,
HandshakeTimeout: 20 * time.Second,
ResponseTimeout: 40 * time.Second,
}
// HttpClient is used for sending http request
// HttpClient is used for sending http request.
type HttpClient struct {
*http.Client
TLS *tls.Config
Request *http.Request
Config HttpClientConfig
Context context.Context
}
// NewHttpClient make a HttpClient instance
// NewHttpClient make a HttpClient instance.
func NewHttpClient() *HttpClient {
client := &HttpClient{
Client: &http.Client{
@@ -141,7 +143,7 @@ func NewHttpClient() *HttpClient {
return client
}
// NewHttpClientWithConfig make a HttpClient instance with pass config
// NewHttpClientWithConfig make a HttpClient instance with pass config.
func NewHttpClientWithConfig(config *HttpClientConfig) *HttpClient {
if config == nil {
config = defaultHttpClientConfig
@@ -176,6 +178,11 @@ func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, err
rawUrl := request.RawURL
req, err := http.NewRequest(request.Method, rawUrl, bytes.NewBuffer(request.Body))
if client.Context != nil {
req, err = http.NewRequestWithContext(client.Context, request.Method, rawUrl, bytes.NewBuffer(request.Body))
}
if err != nil {
return nil, err
}

View File

@@ -230,7 +230,6 @@ func UploadFile(filepath string, server string) (bool, error) {
}
// DownloadFile will download the file exist in url to a local file.
// Play: todo
func DownloadFile(filepath string, url string) error {
resp, err := http.Get(url)
if err != nil {

View File

@@ -4,7 +4,9 @@
// Package pointer contains some util functions to operate go pointer.
package pointer
import "reflect"
import (
"reflect"
)
// Of returns a pointer to the value `v`.
// Play: https://go.dev/play/p/HFd70x4DrMj
@@ -47,5 +49,10 @@ func ExtractPointer(value any) any {
if t.Kind() != reflect.Pointer {
return value
}
return ExtractPointer(v.Elem().Interface())
if v.Elem().IsValid() {
return ExtractPointer(v.Elem().Interface())
}
return nil
}

View File

@@ -8,22 +8,30 @@ import (
crand "crypto/rand"
"fmt"
"io"
"math"
"math/rand"
"time"
"unsafe"
"github.com/duke-git/lancet/v2/mathutil"
)
const (
Numeral = "0123456789"
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
MaximumCapacity = math.MaxInt>>1 + 1
Numeral = "0123456789"
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
)
var rn = rand.NewSource(time.Now().UnixNano())
func init() {
rand.Seed(time.Now().UnixNano())
}
// RandInt generate random int between min and max, maybe min, not be max.
// RandInt generate random int between [min, max).
// Play: https://go.dev/play/p/pXyyAAI5YxD
func RandInt(min, max int) int {
if min == max {
@@ -37,6 +45,22 @@ func RandInt(min, max int) int {
return rand.Intn(max-min) + min
}
// RandFloat generate random float64 number between [min, max) with specific precision.
// Play: https://go.dev/play/p/zbD_tuobJtr
func RandFloat(min, max float64, precision int) float64 {
if min == max {
return min
}
if max < min {
min, max = max, min
}
n := rand.Float64()*(max-min) + min
return mathutil.RoundToFloat(n, precision)
}
// RandBytes generate random byte slice.
// Play: https://go.dev/play/p/EkiLESeXf8d
func RandBytes(length int) []byte {
@@ -52,19 +76,19 @@ func RandBytes(length int) []byte {
return b
}
// RandString generate random string of specified length.
// RandString generate random alpha string of specified length.
// Play: https://go.dev/play/p/W2xvRUXA7Mi
func RandString(length int) string {
return random(Letters, length)
}
// RandUpper generate a random upper case string.
// RandUpper generate a random upper case string of specified length.
// Play: https://go.dev/play/p/29QfOh0DVuh
func RandUpper(length int) string {
return random(UpperLetters, length)
}
// RandLower generate a random lower case string.
// RandLower generate a random lower case string of specified length.
// Play: https://go.dev/play/p/XJtZ471cmtI
func RandLower(length int) string {
return random(LowwerLetters, length)
@@ -76,24 +100,74 @@ func RandNumeral(length int) string {
return random(Numeral, length)
}
// RandNumeralOrLetter generate a random numeral or letter string.
// RandNumeralOrLetter generate a random numeral or alpha string of specified length.
// Play: https://go.dev/play/p/19CEQvpx2jD
func RandNumeralOrLetter(length int) string {
return random(Numeral+Letters, length)
}
// RandSymbolChar generate a random symbol char of specified length.
// symbol chars: !@#$%^&*()_+-=[]{}|;':\",./<>?.
// Play: https://go.dev/play/p/Im6ZJxAykOm
func RandSymbolChar(length int) string {
return random(SymbolChars, length)
}
// nearestPowerOfTwo 返回一个大于等于cap的最近的2的整数次幂参考java8的hashmap的tableSizeFor函数
func nearestPowerOfTwo(cap int) int {
n := cap - 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
if n < 0 {
return 1
} else if n >= MaximumCapacity {
return MaximumCapacity
}
return n + 1
}
// random generate a random string based on given string range.
func random(s string, length int) string {
b := make([]byte, length)
// fix: https://github.com/duke-git/lancet/issues/75
// r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := range b {
b[i] = s[rand.Int63()%int64(len(s))]
// 仿照strings.Builder
// 创建一个长度为 length 的字节切片
bytes := make([]byte, length)
strLength := len(s)
if strLength <= 0 {
return ""
} else if strLength == 1 {
for i := 0; i < length; i++ {
bytes[i] = s[0]
}
return *(*string)(unsafe.Pointer(&bytes))
}
return string(b)
// s的字符需要使用多少个比特位数才能表示完
// letterIdBits := int(math.Ceil(math.Log2(strLength))),下面比上面的代码快
letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(strLength))))
// 最大的字母id掩码
var letterIdMask int64 = 1<<letterIdBits - 1
// 可用次数的最大值
letterIdMax := 63 / letterIdBits
// 循环生成随机字符串
for i, cache, remain := length-1, rn.Int63(), letterIdMax; i >= 0; {
// 检查随机数生成器是否用尽所有随机数
if remain == 0 {
cache, remain = rn.Int63(), letterIdMax
}
// 从可用字符的字符串中随机选择一个字符
if idx := int(cache & letterIdMask); idx < strLength {
bytes[i] = s[idx]
i--
}
// 右移比特位数,为下次选择字符做准备
cache >>= letterIdBits
remain--
}
// 仿照strings.Builder用unsafe包返回一个字符串避免拷贝
// 将字节切片转换为字符串并返回
return *(*string)(unsafe.Pointer(&bytes))
}
// UUIdV4 generate a random UUID of version 4 according to RFC 4122.
@@ -136,3 +210,21 @@ func RandUniqueIntSlice(n, min, max int) []int {
return nums
}
// RandFloats generate a slice of random float64 numbers of length n that do not repeat.
// Play: https://go.dev/play/p/I3yndUQ-rhh
func RandFloats(n int, min, max float64, precision int) []float64 {
nums := make([]float64, n)
used := make(map[float64]struct{}, n)
for i := 0; i < n; {
r := RandFloat(min, max, precision)
if _, use := used[r]; use {
continue
}
used[r] = struct{}{}
nums[i] = r
i++
}
return nums
}

View File

@@ -3,6 +3,7 @@ package random
import (
"fmt"
"regexp"
"strconv"
)
func ExampleRandInt() {
@@ -134,3 +135,53 @@ func ExampleRandUniqueIntSlice() {
// Output:
// ok
}
func ExampleRandSymbolChar() {
pattern := `^[\W|_]+$`
reg := regexp.MustCompile(pattern)
s := RandSymbolChar(6)
result1 := reg.MatchString(s)
result2 := len(s)
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// 6
}
func ExampleRandFloat() {
pattern := `^[\d{1}.\d{2}]+$`
reg := regexp.MustCompile(pattern)
num := RandFloat(1.0, 5.0, 2)
// check num is a random float in [1.0, 5.0)
result1 := num >= 1.0 && num < 5.0
result2 := reg.MatchString(strconv.FormatFloat(num, 'f', -1, 64))
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// true
}
func ExampleRandFloats() {
isInRange := true
numbers := RandFloats(5, 1.0, 5.0, 2)
for _, n := range numbers {
isInRange = (n >= 1.0 && n < 5.0)
}
fmt.Println(isInRange)
fmt.Println(len(numbers))
// Output:
// true
// 5
}

View File

@@ -154,3 +154,45 @@ func hasDuplicate(arr []int) bool {
}
return false
}
func TestRandSymbolChar(t *testing.T) {
t.Parallel()
pattern := `^[\W|_]+$`
reg := regexp.MustCompile(pattern)
symbolChars := RandSymbolChar(10)
assert := internal.NewAssert(t, "TestRandSymbolChar")
assert.Equal(10, len(symbolChars))
assert.Equal(true, reg.MatchString(symbolChars))
}
func TestRandFloat(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandFloat")
r1 := RandFloat(1.1, 10.1, 2)
assert.GreaterOrEqual(r1, 1.1)
assert.Less(r1, 10.1)
r2 := RandFloat(1.1, 1.1, 2)
assert.Equal(1.1, r2)
r3 := RandFloat(10.1, 1.1, 2)
assert.GreaterOrEqual(r1, 1.1)
assert.Less(r3, 10.1)
}
func TestRandFloats(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRandFloats")
numbers := RandFloats(5, 1.0, 5.0, 2)
for _, n := range numbers {
assert.Equal(true, (n >= 1.0 && n < 5.0))
}
assert.Equal(len(numbers), 5)
}

View File

@@ -8,6 +8,8 @@ import (
"context"
"errors"
"fmt"
"math"
"math/rand"
"reflect"
"runtime"
"strings"
@@ -18,14 +20,14 @@ const (
// DefaultRetryTimes times of retry
DefaultRetryTimes = 5
// DefaultRetryDuration time duration of two retries
DefaultRetryDuration = time.Second * 3
DefaultRetryLinearInterval = time.Second * 3
)
// RetryConfig is config for retry
type RetryConfig struct {
context context.Context
retryTimes uint
retryDuration time.Duration
context context.Context
retryTimes uint
backoffStrategy BackoffStrategy
}
// RetryFunc is function that retry executes
@@ -42,11 +44,59 @@ func RetryTimes(n uint) Option {
}
}
// RetryDuration set duration of retries.
// Play: https://go.dev/play/p/nk2XRmagfVF
func RetryDuration(d time.Duration) Option {
// RetryWithCustomBackoff set abitary custom backoff strategy
// Play: todo
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
if backoffStrategy == nil {
panic("programming error: backoffStrategy must be not nil")
}
return func(rc *RetryConfig) {
rc.retryDuration = d
rc.backoffStrategy = backoffStrategy
}
}
// RetryWithLinearBackoff set linear strategy backoff
// Play: todo
func RetryWithLinearBackoff(interval time.Duration) Option {
if interval <= 0 {
panic("programming error: retry interval should not be lower or equal to 0")
}
return func(rc *RetryConfig) {
rc.backoffStrategy = &linear{
interval: interval,
}
}
}
// RetryWithExponentialWithJitterBackoff set exponential strategy backoff
// Play: todo
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option {
if interval <= 0 {
panic("programming error: retry interval should not be lower or equal to 0")
}
if maxJitter < 0 {
panic("programming error: retry maxJitter should not be lower to 0")
}
if base%2 == 0 {
return func(rc *RetryConfig) {
rc.backoffStrategy = &shiftExponentialWithJitter{
interval: interval,
maxJitter: maxJitter,
shifter: uint64(math.Log2(float64(base))),
}
}
}
return func(rc *RetryConfig) {
rc.backoffStrategy = &exponentialWithJitter{
interval: interval,
base: time.Duration(base),
maxJitter: maxJitter,
}
}
}
@@ -63,21 +113,26 @@ func Context(ctx context.Context) Option {
// Play: https://go.dev/play/p/nk2XRmagfVF
func Retry(retryFunc RetryFunc, opts ...Option) error {
config := &RetryConfig{
retryTimes: DefaultRetryTimes,
retryDuration: DefaultRetryDuration,
context: context.TODO(),
retryTimes: DefaultRetryTimes,
context: context.TODO(),
}
for _, opt := range opts {
opt(config)
}
if config.backoffStrategy == nil {
config.backoffStrategy = &linear{
interval: DefaultRetryLinearInterval,
}
}
var i uint
for i < config.retryTimes {
err := retryFunc()
if err != nil {
select {
case <-time.After(config.retryDuration):
case <-time.After(config.backoffStrategy.CalculateInterval()):
case <-config.context.Done():
return errors.New("retry is cancelled")
}
@@ -93,3 +148,58 @@ func Retry(retryFunc RetryFunc, opts ...Option) error {
return fmt.Errorf("function %s run failed after %d times retry", funcName, i)
}
// BackoffStrategy is an interface that defines a method for calculating backoff intervals.
type BackoffStrategy interface {
// CalculateInterval returns the time.Duration after which the next retry attempt should be made.
CalculateInterval() time.Duration
}
// linear is a struct that implements the BackoffStrategy interface using a linear backoff strategy.
type linear struct {
// interval specifies the fixed duration to wait between retry attempts.
interval time.Duration
}
// CalculateInterval calculates the next interval returns a constant.
func (l *linear) CalculateInterval() time.Duration {
return l.interval
}
// exponentialWithJitter is a struct that implements the BackoffStrategy interface using a exponential backoff strategy.
type exponentialWithJitter struct {
base time.Duration // base is the multiplier for the exponential backoff.
interval time.Duration // interval is the current backoff interval, which will be adjusted over time.
maxJitter time.Duration // maxJitter is the maximum amount of jitter to apply to the backoff interval.
}
// CalculateInterval calculates the next backoff interval with jitter and updates the interval.
func (e *exponentialWithJitter) CalculateInterval() time.Duration {
current := e.interval
e.interval = e.interval * e.base
return current + jitter(e.maxJitter)
}
// shiftExponentialWithJitter is a struct that implements the BackoffStrategy interface using a exponential backoff strategy.
type shiftExponentialWithJitter struct {
interval time.Duration // interval is the current backoff interval, which will be adjusted over time.
maxJitter time.Duration // maxJitter is the maximum amount of jitter to apply to the backoff interval.
shifter uint64 // shift by n faster than multiplication
}
// CalculateInterval calculates the next backoff interval with jitter and updates the interval.
// Uses shift instead of multiplication
func (e *shiftExponentialWithJitter) CalculateInterval() time.Duration {
current := e.interval
e.interval = e.interval << e.shifter
return current + jitter(e.maxJitter)
}
// Jitter adds a random duration, up to maxJitter,
// to the current interval to introduce randomness and avoid synchronized patterns in retry behavior
func jitter(maxJitter time.Duration) time.Duration {
if maxJitter == 0 {
return 0
}
return time.Duration(rand.Int63n(int64(maxJitter)) + 1)
}

View File

@@ -20,7 +20,7 @@ func ExampleContext() {
}
Retry(increaseNumber,
RetryDuration(time.Microsecond*50),
RetryWithLinearBackoff(time.Microsecond*50),
Context(ctx),
)
@@ -30,7 +30,7 @@ func ExampleContext() {
// 4
}
func ExampleRetryDuration() {
func ExampleRetryWithLinearBackoff() {
number := 0
increaseNumber := func() error {
number++
@@ -40,7 +40,57 @@ func ExampleRetryDuration() {
return errors.New("error occurs")
}
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50))
err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50))
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
type ExampleCustomBackoffStrategy struct {
interval time.Duration
}
func (c *ExampleCustomBackoffStrategy) CalculateInterval() time.Duration {
return c.interval + 1
}
func ExampleRetryWithCustomBackoff() {
number := 0
increaseNumber := func() error {
number++
if number == 3 {
return nil
}
return errors.New("error occurs")
}
err := Retry(increaseNumber, RetryWithCustomBackoff(&ExampleCustomBackoffStrategy{interval: time.Microsecond * 50}))
if err != nil {
return
}
fmt.Println(number)
// Output:
// 3
}
func ExampleRetryWithExponentialWithJitterBackoff() {
number := 0
increaseNumber := func() error {
number++
if number == 3 {
return nil
}
return errors.New("error occurs")
}
err := Retry(increaseNumber, RetryWithExponentialWithJitterBackoff(time.Microsecond*50, 2, time.Microsecond*25))
if err != nil {
return
}
@@ -81,7 +131,7 @@ func ExampleRetry() {
return errors.New("error occurs")
}
err := Retry(increaseNumber, RetryDuration(time.Microsecond*50))
err := Retry(increaseNumber, RetryWithLinearBackoff(time.Microsecond*50))
if err != nil {
return
}

Some files were not shown because too many files have changed in this diff Show More