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

Compare commits

..

54 Commits

Author SHA1 Message Date
dudaodong
967e6a3493 doc: update doc for v2.3.1 2024-05-14 10:47:27 +08:00
dudaodong
5b24801e49 merge qa branch 2024-05-14 10:11:29 +08:00
dudaodong
974ba525a6 Merge branch 'rc' into v2 2024-05-14 10:10:45 +08:00
chentong
f0235c40b6 fix: json tag omitempty convert error (#218) 2024-05-14 10:08:56 +08:00
dudaodong
712a215ea6 reset 2024-05-14 10:02:56 +08:00
dudaodong
7893f828d3 fix: fix get tag failed 2024-05-13 17:49:34 +08:00
Yang Li
53fa210f09 refactor slice.Unique() (#215) 2024-05-09 10:43:59 +08:00
dudaodong
de9ee08be4 test: update net error handle 2024-04-18 14:23:36 +08:00
dudaodong
5381450bea feat: fix email validation failed 2024-04-18 14:18:33 +08:00
Cannian
6853d627f4 refactor(slice): optimize function (#211) 2024-04-06 09:16:28 +08:00
Joker-desire
e461acdb72 fix(netutil): Add proxy IP to send request (#210)
* fix(netutil): Add proxy ip to send request

* fix(netutil): Add proxy IP to send request

---------

Co-authored-by: 杨崟 <yangyin@addcn.com>
2024-04-03 16:52:53 +08:00
dudaodong
2a796adf85 fix: support nest struct in StructToUrlValues 2024-04-02 17:38:40 +08:00
Cannian
5e6e8d82a8 feat(maputil): add ToSortedSlicesDefault method and ToSortedSlicesWithComparator method (#206) 2024-03-31 21:04:55 +08:00
Cannian
e9280b8c25 add Concat method (#204)
* feat(strutil): add Concat method

* feat(strutil): add Concat method
2024-03-25 10:26:37 +08:00
dudaodong
bb6f10a1fb rename CONTRIBUTING file 2024-03-17 10:30:24 +08:00
dudaodong
33b4cffe60 rename CONTRIBUTING file 2024-03-17 10:30:07 +08:00
Cannian
2b765b49e0 refactor(set): pop method randomly removes an element and return (#202) 2024-03-17 10:28:25 +08:00
dudaodong
004dbdc32e doc: update doc for RightPadding and LeftPadding 2024-03-12 09:52:02 +08:00
donutloop
ab50e8120a Slice: padding (#201)
* LeftPadding adds padding to the left begin of a slice.
* RightPadding adds padding to the right end of a slice.
2024-03-10 21:24:17 +08:00
dudaodong
73c97af7d8 doc: update doc for Break function 2024-03-08 21:39:06 +08:00
donutloop
5e8a065eaa Slice: break (#200)
Splits a slice into two based on a predicate function. It starts appending to the second slice after the first element that matches the predicate. All elements after the first match are included in the second slice, regardless of whether they match the predicate or not.
2024-03-07 14:11:19 +08:00
dudaodong
aa74400607 doc: fix doc text error 2024-03-06 16:19:20 +08:00
dudaodong
a6eaaef563 doc: add go playground demo 2024-03-06 15:28:55 +08:00
dudaodong
1b31014f81 doc: add go playground demo 2024-03-06 14:52:35 +08:00
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
64 changed files with 5059 additions and 311 deletions

View File

@@ -3,9 +3,11 @@ on:
push:
branches:
- main
- rc
pull_request:
branches:
- main
- rc
jobs:
build:
runs-on: ubuntu-latest

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

@@ -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.9-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.1-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)
@@ -38,7 +38,7 @@
go get github.com/duke-git/lancet/v2 // will install latest version of v2.x.x
```
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.2. </b>
2. <b>For users who use version below go1.18, you should install v1.x.x. The latest of v1.x.x is v1.4.3. </b>
```go
go get github.com/duke-git/lancet // below go1.18, install latest version of v1.x.x
@@ -318,6 +318,14 @@ import "github.com/duke-git/lancet/v2/convertor"
- **<big>GbkToUtf8</big>** : converts GBK encoding data to utf8 encoding data.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#GbkToUtf8)]
[[play](https://go.dev/play/p/OphmHCN_9u8)]
- **<big>ToStdBase64</big>** : converts a value to a string encoded in standard Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToStdBase64)]
- **<big>ToUrlBase64</big>** : converts a value to a string encoded in url Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToUrlBase64)]
- **<big>ToRawStdBase64</big>** : converts a value to a string encoded in raw standard Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawStdBase64)]
- **<big>ToRawUrlBase64</big>** : converts a value to a string encoded in raw url Base64.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/convertor.md#ToRawUrlBase64)]
<h3 id="cryptor"> 6. Cryptor package is for data encryption and decryption.&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -652,9 +660,12 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>CreateDir</big>** : create directory in absolute path.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CreateDir)]
[[play](https://go.dev/play/p/qUuCe1OGQnM)]
- **<big>CopyFile</big>** :copy src file to dest file.
- **<big>CopyFile</big>** : copy src file to dest file.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CopyFile)]
[[play](https://go.dev/play/p/Jg9AMJMLrJi)]
- **<big>CopyDir</big>** : copy src directory to dest directory.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#CopyDir)]
[[play](https://go.dev/play/p/YAyFTA_UuPb)]
- **<big>FileMode</big>** : return file's mode and permission.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#FileMode)]
[[play](https://go.dev/play/p/2l2hI42fA3p)]
@@ -721,6 +732,12 @@ 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)]
[[play](https://go.dev/play/p/r0hPmKWhsgf)]
- **<big>ParallelChunkRead</big>** : reads the file in parallel and send each chunk of lines to the specified channel.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#ParallelChunkRead)]
[[play](https://go.dev/play/p/teMXnCsdSEw)]
<h3 id="formatter"> 10. Formatter contains some functions for data formatting. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -784,10 +801,32 @@ import "github.com/duke-git/lancet/v2/function"
- **<big>Pipeline</big>** : takes a list of functions and returns a function whose param will be passed into the functions one by one.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Pipeline)]
[[play](https://go.dev/play/p/mPdUVvj6HD6)]
- **<big>AcceptIf</big>** : returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#AcceptIf)]
[[play](https://go.dev/play/p/XlXHHtzCf7d)]
- **<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)]
[[play](https://go.dev/play/p/dTBHJMQ0zD2)]
- **<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)]
[[play](https://go.dev/play/p/LitCIsDFNDA)]
- **<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)]
[[play](https://go.dev/play/p/jbI8BtgFnVE)]
- **<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)]
[[play](https://go.dev/play/p/2KdCoBEOq84)]
- **<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)]
[[play](https://go.dev/play/p/Rb-FdNGpgSO)]
- **<big>Xnor</big>** : returns a composed predicate that represents the logical XNOR of a list of predicates.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/function.md#Xnor)]
[[play](https://go.dev/play/p/FJxko8SFbqc)]
- **<big>Watcher</big>** : Watcher is used for record code execution time. can start/stop/reset the watch timer. get the elapsed time of function execution.
[[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
@@ -862,6 +901,10 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>HasKey</big>** : checks if map has key or not.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#HasKey)]
[[play](https://go.dev/play/p/isZZHOsDhFc)]
- **<big>ToSortedSlicesDefault</big>** : converts a map to two slices sorted by key: one for the keys and another for the values.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesDefault)]
- **<big>ToSortedSlicesWithComparator</big>** : converts a map to two slices sorted by key and using a custom comparison function: one for the keys and another for the values.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#ToSortedSlicesWithComparator)]
- **<big>NewConcurrentMap</big>** : creates a ConcurrentMap with specific shard count.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/maputil.md#NewConcurrentMap)]
[[play](https://go.dev/play/p/3PenTPETJT0)]
@@ -931,6 +974,18 @@ 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)]
[[play](https://go.dev/play/p/8hOeSADZPCo)]
- **<big>CeilToString</big>** : round float up n decimal places, then conver to string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#CeilToString)]
[[play](https://go.dev/play/p/wy5bYEyUKKG)]
- **<big>FloorToFloat</big>** : round float down n decimal places.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#FloorToFloat)]
[[play](https://go.dev/play/p/vbCBrQHZEED)]
- **<big>FloorToString</big>** : round float down n decimal places, then conver to string.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#FloorToString)]
[[play](https://go.dev/play/p/Qk9KPd2IdDb)]
- **<big>Range</big>** : Creates a slice of numbers from start with specified count, element step is 1.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Range)]
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
@@ -970,6 +1025,10 @@ import "github.com/duke-git/lancet/v2/mathutil"
- **<big>Abs</big>** : returns the absolute value of param number.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Sum)]
[[play](https://go.dev/play/p/fsyBh1Os-1d)]
- **<big>Div</big>** : returns the result of x divided by y.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/mathutil.md#Div)]
[[play](https://go.dev/play/p/WLxDdGXXYat)]
<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>
@@ -1139,6 +1198,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>
@@ -1364,6 +1433,16 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Random</big>** : get a random item of slice, return its index, when slice is empty, return -1.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Random)]
[[play](https://go.dev/play/p/UzpGQptWppw)]
- **<big>SetToDefaultIf</big>** : set elements to their default value if they match the given predicate.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#SetToDefaultIf)]
[[play](https://go.dev/play/p/9AXGlPRC0-A)]
- **<big>Break</big>** : breaks a list into two parts at the point where the predicate for the first time is true.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Break)]
- **<big>RightPadding</big>** : adds padding to the right end of a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#RightPadding)]
- **<big>LeftPadding</big>** : adds padding to the left begin of a slice.
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#LeftPadding)]
<h3 id="stream"> 19. Stream package implements a sequence of elements supporting sequential and operations. this package is an experiment to explore if stream in go can work as the way java does. its function is very limited. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1609,6 +1688,12 @@ 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)]
[[play](https://go.dev/play/p/EDbaRvjeNsv)]
- **<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)]
[[play](https://go.dev/play/p/glNdQEA9HUi)]
<h3 id="system"> 22. System package contain some functions about os, runtime, shell command. &nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">index</a></h3>
@@ -1949,7 +2034,7 @@ import "github.com/duke-git/lancet/v2/xerror"
## How to Contribute
#### [Contributing Guide](./CONTRIBUTING.en-US.md)
#### [Contributing Guide](./CONTRIBUTING.md)
## 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.9-green.svg)](https://github.com/duke-git/lancet/releases)
[![Release](https://img.shields.io/badge/release-2.3.1-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>
@@ -649,9 +659,12 @@ import "github.com/duke-git/lancet/v2/fileutil"
- **<big>CreateDir</big>** : 创建嵌套目录,例如/a/, /a/b/。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CreateDir)]
[[play](https://go.dev/play/p/qUuCe1OGQnM)]
- **<big>CopyFile</big>** :拷贝文件,会覆盖原有的文件。
- **<big>CopyFile</big>** : 拷贝文件,会覆盖原有的文件。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CopyFile)]
[[play](https://go.dev/play/p/Jg9AMJMLrJi)]
- **<big>CopyDir</big>** : 拷贝目录。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#CopyDir)]
[[play](https://go.dev/play/p/YAyFTA_UuPb)]
- **<big>FileMode</big>** : 获取文件 mode 信息。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#FileMode)]
[[play](https://go.dev/play/p/2l2hI42fA3p)]
@@ -718,6 +731,14 @@ 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)]
[[play](https://go.dev/play/p/r0hPmKWhsgf)]
- **<big>ParallelChunkRead</big>** : 并行读取文件并将每个块的行发送到指定通道。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ParallelChunkRead)]
[[play](https://go.dev/play/p/teMXnCsdSEw)]
<h3 id="formatter"> 10. formatter 格式化器包含一些数据格式化处理方法。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -781,10 +802,33 @@ 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)]
[[play](https://go.dev/play/p/XlXHHtzCf7d)]
- **<big>And</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑and操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#And)]
[[play](https://go.dev/play/p/dTBHJMQ0zD2)]
- **<big>Or</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑or操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Or)]
[[play](https://go.dev/play/p/LitCIsDFNDA)]
- **<big>Negate</big>** : 返回一个谓词函数,该谓词函数表示当前谓词的逻辑否定。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Negate)]
[[play](https://go.dev/play/p/jbI8BtgFnVE)]
- **<big>Nor</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑非或nor的操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nor)]
[[play](https://go.dev/play/p/2KdCoBEOq84)]
- **<big>Nand</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑非与nand的操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Nand)]
[[play](https://go.dev/play/p/Rb-FdNGpgSO)]
- **<big>Xnor</big>** : 返回一个复合谓词判断函数该判断函数表示一组谓词的逻辑异或xnor的操作。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Xnor)]
[[play](https://go.dev/play/p/FJxko8SFbqc)]
- **<big>Watcher</big>** : Watcher 用于记录代码执行时间。可以启动/停止/重置手表定时器。获取函数执行的时间。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/function.md#Watcher)]
[[play](https://go.dev/play/p/l2yrOpCLd1I)]
<h3 id="maputil"> 12. maputil 包括一些操作 map 的函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
@@ -859,6 +903,10 @@ import "github.com/duke-git/lancet/v2/maputil"
- **<big>HasKey</big>** : 检查 map 是否包含某个 key。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#HasKey)]
[[play](https://go.dev/play/p/isZZHOsDhFc)]
- **<big>ToSortedSlicesDefault</big>** : 将map的key和value转化成两个根据key的值从小到大排序的切片value切片中元素的位置与key对应。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesDefault)]
- **<big>ToSortedSlicesWithComparator</big>** : 将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片value切片中元素的位置与key对应。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#ToSortedSlicesWithComparator)]
- **<big>NewConcurrentMap</big>** : ConcurrentMap 协程安全的 map 结构。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/maputil.md#NewConcurrentMap)]
[[play](https://go.dev/play/p/3PenTPETJT0)]
@@ -928,6 +976,18 @@ 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)]
[[play](https://go.dev/play/p/8hOeSADZPCo)]
- **<big>CeilToString</big>** : 向上舍入进一法保留n位小数返回字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#CeilToString)]
[[play](https://go.dev/play/p/wy5bYEyUKKG)]
- **<big>FloorToFloat</big>** : 向下舍入去尾法保留n位小数。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToFloat)]
[[play](https://go.dev/play/p/vbCBrQHZEED)]
- **<big>FloorToString</big>** : 向下舍入去尾法保留n位小数返回字符串。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#FloorToString)]
[[play](https://go.dev/play/p/Qk9KPd2IdDb)]
- **<big>Range</big>** : 根据指定的起始值和数量,创建一个数字切片。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/mathutil.md#Range)]
[[play](https://go.dev/play/p/9ke2opxa8ZP)]
@@ -967,6 +1027,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)]
[[play](https://go.dev/play/p/WLxDdGXXYat)]
<h3 id="netutil"> 14. netutil 网络包支持获取 ip 地址,发送 http 请求。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1134,6 +1198,16 @@ import "github.com/duke-git/lancet/v2/retry"
- **<big>RetryTimes</big>** : 设置重试次数,默认 5。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryTimes)]
[[play](https://go.dev/play/p/ssfVeU2SwLO)]
- **<big>BackoffStrategy</big>** : 定义计算退避间隔的方法的接口。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#BackoffStrategy)]
- **<big>RetryWithCustomBackoff</big>** : 设置自定义退避策略。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithCustomBackoff)]
- **<big>RetryWithLinearBackoff</big>** : 设置线性策略退避。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithLinearBackoff)]
- **<big>RetryWithExponentialWithJitterBackoff</big>** : 设置指数策略退避。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/retry.md#RetryWithExponentialWithJitterBackoff)]
<h3 id="slice"> 18. slice 包含操作切片的方法集合。&nbsp; &nbsp; &nbsp; &nbsp; <a href="#index">回到目录</a></h3>
@@ -1358,7 +1432,15 @@ import "github.com/duke-git/lancet/v2/slice"
- **<big>Random</big>** : 随机返回切片中元素以及下标, 当切片长度为0时返回下标-1。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Random)]
[[play](https://go.dev/play/p/UzpGQptWppw)]
- **<big>SetToDefaultIf</big>** : 根据给定给定的predicate判定函数来修改切片中的元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#SetToDefaultIf)]
[[play](https://go.dev/play/p/9AXGlPRC0-A)]
- **<big>Break</big>** : 根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Break)]
- **<big>RightPadding</big>** : 在切片的右部添加元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#RightPadding)]
- **<big>LeftPadding</big>** : 在切片的左部添加元素。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#LeftPadding)]
<h3 id="stream"> 19. stream 流,该包仅验证简单的 stream 实现,功能有限。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
@@ -1448,6 +1530,7 @@ import "github.com/duke-git/lancet/v2/stream"
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/stream.md#ToSlice)]
[[play](https://go.dev/play/p/jI6_iZZuVFE)]
<h3 id="structs"> 20. structs 提供操作 struct, tag, field 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>
```go
@@ -1607,6 +1690,13 @@ 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)]
[[play](https://go.dev/play/p/EDbaRvjeNsv)]
- **<big>HammingDistance</big>** : 计算两个字符串之间的汉明距离。
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/strutil.md#HammingDistance)]
[[play](https://go.dev/play/p/glNdQEA9HUi)]
<h3 id="system"> 22. system 包含 os, runtime, shell command 的相关函数。&nbsp; &nbsp; &nbsp; &nbsp;<a href="#index">回到目录</a></h3>

View File

@@ -6,6 +6,7 @@ package convertor
import (
"bytes"
"encoding/base64"
"encoding/binary"
"encoding/gob"
"encoding/json"
@@ -394,3 +395,91 @@ func GbkToUtf8(bs []byte) ([]byte, error) {
b, err := io.ReadAll(r)
return b, err
}
// ToStdBase64 convert data to standard base64 encoding.
// Play: https://go.dev/play/p/_fLJqJD3NMo
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.
// Play: https://go.dev/play/p/C_d0GlvEeUR
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.
// Play: https://go.dev/play/p/wSAr3sfkDcv
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.
// Play: https://go.dev/play/p/HwdDPFcza1O
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)

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

@@ -4,19 +4,21 @@
// Package datastructure contains some data structure. Set is a data container, like slice, but element of set is not duplicate.
package datastructure
import "sort"
// Set is a data container, like slice, but element of set is not duplicate.
type Set[T comparable] map[T]struct{}
// New create a instance of set from given values.
func New[T comparable](items ...T) Set[T] {
set := make(Set[T])
set := make(Set[T], len(items))
set.Add(items...)
return set
}
// FromSlice create a set from given slice.
func FromSlice[T comparable](items []T) Set[T] {
set := make(Set[T])
set := make(Set[T], len(items))
for _, item := range items {
set.Add(item)
}
@@ -77,8 +79,7 @@ func (s Set[T]) ContainAll(other Set[T]) bool {
// Clone return a copy of set
func (s Set[T]) Clone() Set[T] {
set := New[T]()
set.Add(s.Values()...)
set := FromSlice(s.ToSlice())
return set
}
@@ -116,14 +117,11 @@ func (s Set[T]) Size() int {
}
// Values return all values of set
// Deprecated: Values function is deprecated and will be removed in future versions. Please use ToSlice() function instead.
//
// The ToSlice() function provides the same functionality as Values and returns a slice containing all values of the set.
func (s Set[T]) Values() []T {
result := make([]T, 0, len(s))
s.Iterate(func(value T) {
result = append(result, value)
})
return result
return s.ToSlice()
}
// Union creates a new set contain all element of set s and other
@@ -163,7 +161,7 @@ func (s Set[T]) SymmetricDifference(other Set[T]) Set[T] {
return set
}
// Minus creates an set of whose element in origin set but not in compared set
// Minus creates a set of whose element in origin set but not in compared set
func (s Set[T]) Minus(comparedSet Set[T]) Set[T] {
set := New[T]()
@@ -189,11 +187,34 @@ func (s Set[T]) EachWithBreak(iteratee func(item T) bool) {
// Pop delete the top element of set then return it, if set is empty, return nil-value of T and false.
func (s Set[T]) Pop() (v T, ok bool) {
if len(s) > 0 {
items := s.Values()
item := items[len(s)-1]
delete(s, item)
return item, true
for item := range s {
v = item
delete(s, item)
return v, true
}
}
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,6 +1,8 @@
package datastructure
import (
"reflect"
"sort"
"testing"
"github.com/duke-git/lancet/v2/internal"
@@ -241,22 +243,93 @@ func TestEachWithBreak(t *testing.T) {
// assert.Equal(6, sum)
}
// func TestPop(t *testing.T) {
// assert := internal.NewAssert(t, "TestPop")
func TestPop(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSet_Pop")
// s := New[int]()
s := New[int]()
// val, ok := s.Pop()
// assert.Equal(0, val)
// assert.Equal(false, ok)
val, ok := s.Pop()
assert.Equal(0, val)
assert.Equal(false, ok)
// s.Add(1)
// s.Add(2)
// s.Add(3)
s = New(1, 2, 3, 4, 5)
sl := s.ToSlice()
// // s = New(1, 2, 3, 4, 5)
val, ok = s.Pop()
assert.Equal(false, s.Contain(val))
assert.Equal(true, ok)
assert.Equal(len(sl)-1, s.Size())
// val, ok = s.Pop()
// assert.Equal(3, val)
// assert.Equal(true, ok)
// }
var found bool
for _, v := range sl {
if v == val {
found = true
}
}
assert.Equal(true, found)
}
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

@@ -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>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/_fLJqJD3NMo)</span></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>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/C_d0GlvEeUR)</span></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>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wSAr3sfkDcv)</span></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>函数签名:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/HwdDPFcza1O)</span></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.ToRawUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
```

View File

@@ -31,6 +31,7 @@ import (
- [Iterate](#Iterate)
- [Keys](#Keys)
- [Values](#Values)
- [FilterByValue](#FilterByValue)
<div STYLE="page-break-after: always;"></div>
@@ -276,7 +277,7 @@ func main() {
### <span id="Values">Values</span>
<p>返回hashmap所有值的切片 (随机顺序).</p>
<p>返回hashmap所有值的切片 (随机顺序)</p>
<b>函数签名:</b>
@@ -306,3 +307,40 @@ func main() {
```
### <span id="FilterByValue">FilterByValue</span>
<p>返回一个过滤后的HashMap。 如果任何值与 perdicate 函数不匹配,则返回 nil否则返回包含选定值的 HashMap。</p>
<b>函数签名:</b>
```go
func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap
```
<b>示例:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := hashmap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
hm.Put("d", 4)
hm.Put("e", 5)
hm.Put("f", 6)
filteredHM := hm.FilterByValue(func(value any) bool {
return value.(int) == 1 || value.(int) == 3
})
fmt.Println(filteredHM.Size()) //2
}
```

View File

@@ -40,6 +40,9 @@ import (
- [Intersection](#Intersection)
- [SymmetricDifference](#SymmetricDifference)
- [Minus](#Minus)
- [Pop](#Pop)
- [ToSlice](#ToSlice)
- [ToSortedSlice](#ToSortedSlice)
<div STYLE="page-break-after: always;"></div>
@@ -52,7 +55,7 @@ import (
<b>函数签名:</b>
```go
type Set[T comparable] map[T]bool
type Set[T comparable] map[T]struct{}
func New[T comparable](items ...T) Set[T]
```
@@ -98,9 +101,10 @@ func main() {
}
```
### <span id="Values">Values</span>
### <span id="Values">Values<sup>deprecated</sup></span>
<p>获取集合中所有元素的切片</p>
<p>获取集合中所有元素的切片<br>
<a href='#ToSlice'>ToSlice()</a> 方法提供与 Values 方法相同的功能</p>
<b>函数签名:</b>
@@ -647,3 +651,58 @@ func main() {
fmt.Println(ok) // true
}
```
### <span id="ToSlice">ToSlice</span>
<p>以切片的形式返回集合中所有的元素(无序)</p>
<b>函数签名:</b>
```go
func (s Set[T]) ToSlice() (v T, ok bool)
```
<b>示例:</b>
```go
func main() {
s := set.New(1, 2, 3, 4, 5)
val := s.ToSlice()
fmt.Println(val) // [2 3 4 5 1]
}
```
### <span id="ToSortedSlice">ToSortedSlice</span>
<p>以切片的形式返回集合中所有的元素(按给定的规则排序)</p>
<b>函数签名:</b>
```go
func (s Set[T]) ToSortedSlice() (v T, ok bool)
```
<b>示例:</b>
```go
func main() {
s1 := set.New(1, 2, 3, 4, 5)
type Person struct {
Name string
Age int
}
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
return v1 < v2
})
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
return v1.Age < v2.Age
})
fmt.Println(res1) // [1 2 3 4 5]
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
}
```

View File

@@ -50,6 +50,8 @@ import (
- [WriteStringToFile](#WriteStringToFile)
- [WriteBytesToFile](#WriteBytesToFile)
- [ReadFile](#ReadFile)
- [ChunkRead](#ChunkRead)
- [ParallelChunkRead](#ParallelChunkRead)
<div STYLE="page-break-after: always;"></div>
@@ -955,9 +957,123 @@ func main() {
if err != nil {
return
}
fmt.Println(string(dat))
// Output:
// User-agent: *
// Disallow: /deny
}
```
### <span id="ChunkRead">ChunkRead</span>
<p>从文件的指定偏移读取块并返回块内所有行。</p>
<b>函数签名:</b>
```go
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/r0hPmKWhsgf)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100
// test1.csv file content:
// Lili,22,female
// Jim,21,male
filePath := "./testdata/test1.csv" // 替换为你的文件路径
f, err := os.Open(filePath)
if err != nil {
return
}
defer f.Close()
var bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultChunkSizeMB*mb)
},
}
lines, err := fileutil.ChunkRead(f, 0, 100, &bufPool)
if err != nil {
return
}
fmt.Println(lines[0])
fmt.Println(lines[1])
// Output:
// Lili,22,female
// Jim,21,male
}
```
### <span id="ParallelChunkRead">ParallelChunkRead</span>
<p>并行读取文件并将每个块的行发送到指定通道。</p>
<b>函数签名:</b>
```go
// filePath:文件路径
// chunkSizeMB: 分块的大小单位MB设置为0时使用默认100MB,设置过大反而不利,视情调整
// maxGoroutine: 并发读取分块的数量设置为0时使用CPU核心数
// linesCh: 用于接收返回结果的通道。
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/teMXnCsdSEw)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
// test1.csv file content:
// Lili,22,female
// Jim,21,male
filePath := "./testdata/test1.csv"
go fileutil.ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
var totalLines int
for lines := range linesCh {
totalLines += len(lines)
for _, line := range lines {
fmt.Println(line)
}
}
fmt.Println(totalLines)
// Output:
// Lili,22,female
// Jim,21,male
// 2
}
```

View File

@@ -39,6 +39,8 @@ import (
- [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand)
- [AcceptIf](#AcceptIf)
<div STYLE="page-break-after: always;"></div>
@@ -422,7 +424,7 @@ func longRunningTask() {
func And[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/dTBHJMQ0zD2)</span></b>
```go
package main
@@ -459,7 +461,7 @@ func main() {
func Or[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/LitCIsDFNDA)</span></b>
```go
package main
@@ -494,7 +496,7 @@ func main() {
func Negate[T any](predicate func(T) bool) func(T) bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/jbI8BtgFnVE)</span></b>
```go
package main
@@ -534,7 +536,7 @@ func main() {
func Nor[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/2KdCoBEOq84)</span></b>
```go
package main
@@ -576,7 +578,7 @@ func main() {
func Nand[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Rb-FdNGpgSO)</span></b>
```go
package main
@@ -613,7 +615,7 @@ func main() {
func Xnor[T any](predicates ...func(T) bool) func(T) bool
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/FJxko8SFbqc)</span></b>
```go
package main
@@ -638,4 +640,54 @@ func main() {
// true
// false
}
```
### <span id="AcceptIf">AcceptIf</span>
<p>AcceptIf函数会返回另一个函数该函数的签名与 apply 函数相同,但同时还会包含一个布尔值来表示成功或失败。</p>
<b>函数签名:</b>
```go
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/XlXHHtzCf7d)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
adder := function.AcceptIf(
function.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

@@ -44,6 +44,8 @@ import (
- [Minus](#Minus)
- [IsDisjoint](#IsDisjoint)
- [HasKey](#HasKey)
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
- [NewConcurrentMap](#NewConcurrentMap)
- [ConcurrentMap_Get](#ConcurrentMap_Get)
- [ConcurrentMap_Set](#ConcurrentMap_Set)
@@ -988,6 +990,98 @@ func main() {
}
```
### <span id="ToSortedSlicesDefault">ToSortedSlicesDefault</span>
<p>将map的key和value转化成两个根据key的值从小到大排序的切片value切片中元素的位置与key对应。</p>
<b>函数签名:</b>
```go
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys, values := ToSortedSlicesDefault(m)
fmt.Println(keys)
fmt.Println(values)
// Output:
// [1 2 3]
// [a b c]
}
```
### <span id="ToSortedSlicesWithComparator">ToSortedSlicesWithComparator</span>
<p>将map的key和value转化成两个使用比较器函数根据key的值自定义排序规则的切片value切片中元素的位置与key对应。</p>
<b>函数签名:</b>
```go
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m1 := map[time.Time]string{
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
}
keys1, values1 := ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
return a.Before(b)
})
m2 := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys2, values2 := ToSortedSlicesWithComparator(m2, func(a, b int) bool {
return a > b
})
fmt.Println(keys2)
fmt.Println(values2)
fmt.Println(keys1)
fmt.Println(values1)
// Output:
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
// [yesterday today tomorrow]
// [3 2 1]
// [c b a]
}
```
### <span id="NewConcurrentMap">NewConcurrentMap</span>
<p>ConcurrentMap协程安全的map结构。</p>

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>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/8hOeSADZPCo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.CeilToFloat(3.14159, 1)
result2 := mathutil.CeilToFloat(3.14159, 2)
result3 := mathutil.CeilToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5
}
```
### <span id="CeilToString">CeilToString</span>
<p>向上舍入进一法保留n位小数返回字符串。</p>
<b>函数签名:</b>
```go
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/wy5bYEyUKKG)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.CeilToString(3.14159, 1)
result2 := mathutil.CeilToString(3.14159, 2)
result3 := mathutil.CeilToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5.0000
}
```
### <span id="FloorToFloat">FloorToFloat</span>
<p>向下舍入去尾法保留n位小数。</p>
<b>函数签名:</b>
```go
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/vbCBrQHZEED)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.FloorToFloat(3.14159, 1)
result2 := mathutil.FloorToFloat(3.14159, 2)
result3 := mathutil.FloorToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5
}
```
### <span id="FloorToString">FloorToString</span>
<p>向下舍入去尾法保留n位小数返回字符串。</p>
<b>函数签名:</b>
```go
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/Qk9KPd2IdDb)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.FloorToString(3.14159, 1)
result2 := mathutil.FloorToString(3.14159, 2)
result3 := mathutil.FloorToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5.0000
}
```
### <span id="Range">Range</span>
<p>根据指定的起始值和数量,创建一个数字切片。</p>
@@ -965,16 +1114,52 @@ 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>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/WLxDdGXXYat)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Div(9, 4)
result2 := mathutil.Div(1, 2)
result3 := mathutil.Div(0, 666)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 2.25
// 0.5
// 0
}
```

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

@@ -93,6 +93,10 @@ import (
- [KeyBy](#KeyBy)
- [Join](#Join)
- [Partition](#Partition)
- [SetToDefaultIf](#SetToDefaultIf)
- [Break](#Break)
- [RightPadding](#RightPadding)
- [LeftPadding](#LeftPadding)
<div STYLE="page-break-after: always;"></div>
@@ -2568,4 +2572,122 @@ func main() {
// Output:
// okk
}
```
### <span id="SetToDefaultIf">SetToDefaultIf</span>
<p>根据给定给定的predicate判定函数来修改切片中的元素。对于满足的元素将其替换为指定的默认值同时保持元素在切片中的位置不变。函数返回修改后的切片以及被修改的元素个数。</p>
<b>函数签名:</b>
```go
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/9AXGlPRC0-A)</span></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
}
```
### <span id="Break">Break</span>
<p>根据判断函数将切片分成两部分。它开始附加到与函数匹配的第一个元素之后的第二个切片。第一个匹配之后的所有元素都包含在第二个切片中,无论它们是否与函数匹配。</p>
<b>函数签名:</b>
```go
func Break[T any](values []T, predicate func(T) bool) ([]T, []T)
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
even := func(n int) bool { return n%2 == 0 }
resultEven, resultAfterFirstEven := slice.Break(nums, even)
fmt.Println(resultEven)
fmt.Println(resultAfterFirstEven)
// Output:
// [1]
// [2 3 4 5]
}
```
<span id="RightPadding">RightPadding</span>
<p>在切片的右部添加元素。</p>
<b>函数签名:</b>
```go
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
}
```
<span id="LeftPadding">LeftPadding</span>
<p>在切片的左部添加元素。</p>
<b>函数签名:</b>
```go
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>示例:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := slice.LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
```

View File

@@ -61,6 +61,8 @@ import (
- [ContainsAny](#ContainsAny)
- [RemoveWhiteSpace](#RemoveWhiteSpace)
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
<div STYLE="page-break-after: always;"></div>
@@ -1474,7 +1476,7 @@ func main() {
func SubInBetween(str string, start string, end string) string
```
<b>示例:</b>
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/EDbaRvjeNsv)</span></b>
```go
import (
@@ -1495,4 +1497,70 @@ func main() {
// abc
// bc
}
```
### <span id="HammingDistance">HammingDistance</span>
<p>计算两个字符串之间的汉明距离。汉明距离是指对应符号不同的位置数。</p>
<b>函数签名:</b>
```go
func HammingDistance(a, b string) (int, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/glNdQEA9HUi)</span></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
}
```
### <span id="Concat">Concat</span>
<p>拼接字符串。length是拼接后字符串的长度如果不确定则传0或负数。</p>
<b>函数签名:</b>
```go
func Concat(length int, str ...string) string
```
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}
```

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>
@@ -567,6 +571,7 @@ func main() {
}
```
### <span id="EncodeByte">EncodeByte</span>
<p>Encode data to byte slice.</p>
@@ -632,69 +637,6 @@ func main() {
}
```
### <span id="DeepClone">DeepClone</span>
<p>Creates a deep copy of passed item, can't clone unexported field of struct.</p>
<b>Signature:</b>
```go
func DeepClone[T any](src T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/j4DP5dquxnk)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
type Struct struct {
Str string
Int int
Float float64
Bool bool
Nil interface{}
unexported string
}
cases := []interface{}{
true,
1,
0.1,
map[string]int{
"a": 1,
"b": 2,
},
&Struct{
Str: "test",
Int: 1,
Float: 0.1,
Bool: true,
Nil: nil,
// unexported: "can't be cloned",
},
}
for _, item := range cases {
cloned := convertor.DeepClone(item)
isPointerEqual := &cloned == &item
fmt.Println(cloned, isPointerEqual)
}
// Output:
// true false
// 1 false
// 0.1 false
// map[a:1 b:2] false
// &{test 1 0.1 true <nil> } false
}
```
### <span id="CopyProperties">CopyProperties</span>
@@ -775,41 +717,6 @@ func main() {
}
```
### <span id="ToInterface">ToInterface</span>
<p>Converts reflect value to its interface type.</p>
<b>Signature:</b>
```go
func ToInterface(v reflect.Value) (value interface{}, ok bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/syqw0-WG7Xd)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
val := reflect.ValueOf("abc")
iVal, ok := convertor.ToInterface(val)
fmt.Printf("%T\n", iVal)
fmt.Printf("%v\n", iVal)
fmt.Println(ok)
// Output:
// string
// abc
// true
}
```
### <span id="Utf8ToGbk">Utf8ToGbk</span>
<p>Converts utf8 encoding data to GBK encoding data.</p>
@@ -875,4 +782,338 @@ 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:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/_fLJqJD3NMo)</span></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:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/C_d0GlvEeUR)</span></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:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wSAr3sfkDcv)</span></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:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/HwdDPFcza1O)</span></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.ToRawUrlBase64(boolVal)
fmt.Println(afterEncode)
errVal := errors.New("err")
afterEncode = convertor.ToRawUrlBase64(errVal)
fmt.Println(afterEncode)
// Output:
// aGVsbG8
// aGVsbG8
// MTIz
// eyJhIjoiaGkiLCJiIjoyLCJjIjp7IkEiOiJoZWxsbyIsIkIiOjN9fQ
// MTIzLjQ1Ng
// dHJ1ZQ
// ZXJy
}
```
### <span id="DeepClone">DeepClone</span>
<p>Creates a deep copy of passed item, can't clone unexported field of struct.</p>
<b>Signature:</b>
```go
func DeepClone[T any](src T) T
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/j4DP5dquxnk)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
type Struct struct {
Str string
Int int
Float float64
Bool bool
Nil interface{}
unexported string
}
cases := []interface{}{
true,
1,
0.1,
map[string]int{
"a": 1,
"b": 2,
},
&Struct{
Str: "test",
Int: 1,
Float: 0.1,
Bool: true,
Nil: nil,
},
}
for _, item := range cases {
cloned := convertor.DeepClone(item)
isPointerEqual := &cloned == &item
fmt.Println(cloned, isPointerEqual)
}
// Output:
// true false
// 1 false
// 0.1 false
// map[a:1 b:2] false
// &{test 1 0.1 true <nil> } false
}
```

View File

@@ -32,6 +32,7 @@ import (
- [Iterate](#Iterate)
- [Keys](#Keys)
- [Values](#Values)
- [FilterByValue](#FilterByValue)
<div STYLE="page-break-after: always;"></div>
@@ -311,4 +312,77 @@ func main() {
}
```
### <span id="FilterByValue">FilterByValue</span>
<p>Returns a filtered HashMap.</p>
<b>Signature:</b>
```go
func (hm *HashMap) FilterByValue(perdicate func(value any) bool) *HashMap
```
<b>Example:</b>
```go
package main
import (
"fmt"
hashmap "github.com/duke-git/lancet/v2/datastructure/hashmap"
)
func main() {
hm := hashmap.NewHashMap()
hm.Put("a", 1)
hm.Put("b", 2)
hm.Put("c", 3)
hm.Put("d", 4)
hm.Put("e", 5)
hm.Put("f", 6)
filteredHM := hm.FilterByValue(func(value any) bool {
return value.(int) == 1 || value.(int) == 3
})
fmt.Println(filteredHM.Size()) //2
}
```
### <span id="ToInterface">ToInterface</span>
<p>Converts reflect value to its interface type.</p>
<b>Signature:</b>
```go
func ToInterface(v reflect.Value) (value interface{}, ok bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/syqw0-WG7Xd)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/convertor"
)
func main() {
val := reflect.ValueOf("abc")
iVal, ok := convertor.ToInterface(val)
fmt.Printf("%T\n", iVal)
fmt.Printf("%v\n", iVal)
fmt.Println(ok)
// Output:
// string
// abc
// true
}
```

View File

@@ -41,6 +41,9 @@ import (
- [Intersection](#Intersection)
- [SymmetricDifference](#SymmetricDifference)
- [Minus](#Minus)
- [Pop](#Pop)
- [ToSlice](#ToSlice)
- [ToSortedSlice](#ToSortedSlice)
<div STYLE="page-break-after: always;"></div>
@@ -53,7 +56,7 @@ import (
<b>Signature:</b>
```go
type Set[T comparable] map[T]bool
type Set[T comparable] map[T]struct{}
func New[T comparable](items ...T) Set[T]
```
@@ -99,9 +102,10 @@ func main() {
}
```
### <span id="Values">Values</span>
### <span id="Values">Values<sup>deprecated</sup></span>
<p>Return slice of all set data</p>
<p>Return slice of all set data.<br>
The <a href='#ToSlice'>ToSlice()</a> function provides the same functionality as Values and returns a slice containing all values of the set.</p>
<b>Signature:</b>
@@ -648,3 +652,58 @@ func main() {
fmt.Println(ok) // true
}
```
### <span id="ToSlice">ToSlice</span>
<p>returns a slice containing all values of the set.</p>
<b>Signature:</b>
```go
func (s Set[T]) ToSlice() (v T, ok bool)
```
<b>Example:</b>
```go
func main() {
s := set.New(1, 2, 3, 4, 5)
val := s.ToSlice()
fmt.Println(val) // [2 3 4 5 1]
}
```
### <span id="ToSortedSlice">ToSortedSlice</span>
<p>returns a sorted slice containing all values of the set</p>
<b>Signature:</b>
```go
func (s Set[T]) ToSortedSlice() (v T, ok bool)
```
<b>Example:</b>
```go
func main() {
s1 := set.New(1, 2, 3, 4, 5)
type Person struct {
Name string
Age int
}
s2 := FromSlice([]Person{{"Tom", 20}, {"Jerry", 18}, {"Spike", 25}})
res1 := s1.ToSortedSlice(func(v1, v2 int) bool {
return v1 < v2
})
res2 := s2.ToSortedSlice(func(v1, v2 Person) bool {
return v1.Age < v2.Age
})
fmt.Println(res1) // [1 2 3 4 5]
fmt.Println(res2) // [{Jerry 18} {Tom 20} {Spike 25}]
}
```

View File

@@ -50,6 +50,8 @@ import (
- [WriteStringToFile](#WriteStringToFile)
- [WriteBytesToFile](#WriteBytesToFile)
- [ReadFile](#ReadFile)
- [ChunkRead](#ChunkRead)
- [ParallelChunkRead](#ParallelChunkRead)
<div STYLE="page-break-after: always;"></div>
@@ -165,7 +167,7 @@ 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>
<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>
@@ -961,3 +963,115 @@ func main() {
// Disallow: /deny
}
```
### <span id="ChunkRead">ChunkRead</span>
<p>reads a block from the file at the specified offset and returns all lines within the block.</p>
<b>Signature :</b>
```go
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/r0hPmKWhsgf)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100
// test1.csv file content:
// Lili,22,female
// Jim,21,male
filePath := "./testdata/test1.csv"
f, err := os.Open(filePath)
if err != nil {
return
}
defer f.Close()
var bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultChunkSizeMB*mb)
},
}
lines, err := fileutil.ChunkRead(f, 0, 100, &bufPool)
if err != nil {
return
}
fmt.Println(lines[0])
fmt.Println(lines[1])
// Output:
// Lili,22,female
// Jim,21,male
}
```
### <span id="ParallelChunkRead">ParallelChunkRead</span>
<p>Reads the file in parallel and send each chunk of lines to the specified channel.</p>
<b>Signature :</b>
```go
// filePath: file path.
// chunkSizeMB: The size of the block (in MB, the default is 100MB when set to 0). Setting it too large will be detrimental. Adjust it as appropriate.
// maxGoroutine: The number of concurrent read chunks, the number of CPU cores used when set to 0.
// linesCh: The channel used to receive the returned results.
func ParallelChunkRead(filePath string, linesCh chan<- []string, chunkSizeMB, maxGoroutine int) error
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/teMXnCsdSEw)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
// test1.csv file content:
// Lili,22,female
// Jim,21,male
filePath := "./testdata/test1.csv"
go fileutil.ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
var totalLines int
for lines := range linesCh {
totalLines += len(lines)
for _, line := range lines {
fmt.Println(line)
}
}
fmt.Println(totalLines)
// Output:
// Lili,22,female
// Jim,21,male
// 2
}
```

View File

@@ -39,6 +39,7 @@ import (
- [Nor](#Nor)
- [Xnor](#Xnor)
- [Nand](#Nand)
- [AcceptIf](#AcceptIf)
<div STYLE="page-break-after: always;"></div>
@@ -422,7 +423,7 @@ func longRunningTask() {
func And[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/dTBHJMQ0zD2)</span></b>
```go
package main
@@ -459,7 +460,7 @@ func main() {
func Or[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/LitCIsDFNDA)</span></b>
```go
package main
@@ -494,7 +495,7 @@ func main() {
func Negate[T any](predicate func(T) bool) func(T) bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/jbI8BtgFnVE)</span></b>
```go
package main
@@ -534,7 +535,7 @@ func main() {
func Nor[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/2KdCoBEOq84)</span></b>
```go
package main
@@ -576,7 +577,7 @@ func main() {
func Nand[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Rb-FdNGpgSO)</span></b>
```go
package main
@@ -613,7 +614,7 @@ func main() {
func Xnor[T any](predicates ...func(T) bool) func(T) bool
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/FJxko8SFbqc)</span></b>
```go
package main
@@ -638,4 +639,55 @@ func main() {
// true
// false
}
```
### <span id="AcceptIf">AcceptIf</span>
<p>AcceptIf returns another function of the same signature as the apply function but also includes a bool value to indicate success or failure. A predicate function that takes an argument of type T and returns a bool. An apply function that also takes an argument of type T and returns a modified value of the same type.</p>
<b>Signature:</b>
```go
func AcceptIf[T any](predicate func(T) bool, apply func(T) T) func(T) (T, bool)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/XlXHHtzCf7d)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/function"
)
func main() {
adder := function.AcceptIf(
function.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

@@ -44,6 +44,8 @@ import (
- [Minus](#Minus)
- [IsDisjoint](#IsDisjoint)
- [HasKey](#HasKey)
- [ToSortedSlicesDefault](#ToSortedSlicesDefault)
- [ToSortedSlicesWithComparator](#ToSortedSlicesWithComparator)
- [NewConcurrentMap](#NewConcurrentMap)
- [ConcurrentMap_Get](#ConcurrentMap_Get)
- [ConcurrentMap_Set](#ConcurrentMap_Set)
@@ -992,6 +994,101 @@ func main() {
}
```
### <span id="ToSortedSlicesDefault">ToSortedSlicesDefault</span>
<p>
Translate the key and value of the map into two slices that are sorted in ascending order according to the keys value, with the position of the elements in the value slice corresponding to the key.</p>
<b>Signature:</b>
```go
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](Todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys, values := ToSortedSlicesDefault(m)
fmt.Println(keys)
fmt.Println(values)
// Output:
// [1 2 3]
// [a b c]
}
```
### <span id="ToSortedSlicesWithComparator">ToSortedSlicesWithComparator</span>
<p>
Translate the key and value of the map into two slices that are sorted according to a custom sorting rule defined by a comparator function based on the key's value, with the position of the elements in the value slice corresponding to the key.
</p>
<b>Signature:</b>
```go
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](Todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/maputil"
)
func main() {
m1 := map[time.Time]string{
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
}
keys1, values1 := ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
return a.Before(b)
})
m2 := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys2, values2 := ToSortedSlicesWithComparator(m2, func(a, b int) bool {
return a > b
})
fmt.Println(keys2)
fmt.Println(values2)
fmt.Println(keys1)
fmt.Println(values1)
// Output:
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
// [yesterday today tomorrow]
// [3 2 1]
// [c b a]
}
```
### <span id="NewConcurrentMap">NewConcurrentMap</span>
<p>ConcurrentMap is like map, but is safe for concurrent use by multiple goroutines.</p>

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:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/8hOeSADZPCo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.CeilToFloat(3.14159, 1)
result2 := mathutil.CeilToFloat(3.14159, 2)
result3 := mathutil.CeilToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5
}
```
### <span id="CeilToString">CeilToString</span>
<p>Round float up n decimal places.</p>
<b>Signature:</b>
```go
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/wy5bYEyUKKG)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.CeilToString(3.14159, 1)
result2 := mathutil.CeilToString(3.14159, 2)
result3 := mathutil.CeilToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.2
// 3.15
// 5.0000
}
```
### <span id="FloorToFloat">FloorToFloat</span>
<p>Round float down n decimal places.</p>
<b>Signature:</b>
```go
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/vbCBrQHZEED)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.FloorToFloat(3.14159, 1)
result2 := mathutil.FloorToFloat(3.14159, 2)
result3 := mathutil.FloorToFloat(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5
}
```
### <span id="FloorToString">FloorToString</span>
<p>Round float down n decimal places.</p>
<b>Signature:</b>
```go
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/Qk9KPd2IdDb)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.FloorToString(3.14159, 1)
result2 := mathutil.FloorToString(3.14159, 2)
result3 := mathutil.FloorToString(5, 4)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 3.1
// 3.14
// 5.0000
}
```
### <span id="Range">Range</span>
<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:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/WLxDdGXXYat)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/mathutil"
)
func main() {
result1 := mathutil.Div(9, 4)
result2 := mathutil.Div(1, 2)
result3 := mathutil.Div(0, 666)
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// 2.25
// 0.5
// 0
}
```

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

@@ -93,6 +93,10 @@ import (
- [KeyBy](#KeyBy)
- [Join](#Join)
- [Partition](#Partition)
- [SetToDefaultIf](#SetToDefaultIf)
- [Break](#Break)
- [RightPadding](#RightPadding)
- [LeftPadding](#LeftPadding)
<div STYLE="page-break-after: always;"></div>
@@ -2565,4 +2569,122 @@ func main() {
// Output:
// okk
}
```
### <span id="SetToDefaultIf">SetToDefaultIf</span>
<p>Sets elements to their default value if they match the given predicate. It retains the positions of the elements in the slice. It returns slice of T and the count of modified slice items</p>
<b>Signature:</b>
```go
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/9AXGlPRC0-A)</span></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
}
```
### <span id="Break">Break</span>
<p>Splits a slice into two based on a predicate function. It starts appending to the second slice after the first element that matches the predicate. All elements after the first match are included in the second slice, regardless of whether they match the predicate or not.</p>
<b>Signature:</b>
```go
func Break[T any](values []T, predicate func(T) bool) ([]T, []T)
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
even := func(n int) bool { return n%2 == 0 }
resultEven, resultAfterFirstEven := slice.Break(nums, even)
fmt.Println(resultEven)
fmt.Println(resultAfterFirstEven)
// Output:
// [1]
// [2 3 4 5]
}
```
<span id="c">RightPadding</span>
<p>RightPadding adds padding to the right end of a slice.</p>
<b>Signature:</b>
```go
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
}
```
<span id="LeftPadding">LeftPadding</span>
<p>LeftPadding adds padding to the left begin of a slice.</p>
<b>Signature:</b>
```go
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T
```
<b>Example:</b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/slice"
)
func main() {
nums := []int{1, 2, 3, 4, 5}
padded := LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}
```

View File

@@ -60,6 +60,9 @@ import (
- [ContainsAll](#ContainsAll)
- [ContainsAny](#ContainsAny)
- [RemoveWhiteSpace](#RemoveWhiteSpace)
- [SubInBetween](#SubInBetween)
- [HammingDistance](#HammingDistance)
- [Concat](#Concat)
<div STYLE="page-break-after: always;"></div>
@@ -1475,7 +1478,7 @@ func main() {
func SubInBetween(str string, start string, end string) string
```
<b>Example:</b>
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/EDbaRvjeNsv)</span></b>
```go
import (
@@ -1496,4 +1499,71 @@ func main() {
// abc
// bc
}
```
### <span id="HammingDistance">HammingDistance</span>
<p>HammingDistance calculates the Hamming distance between two strings. The Hamming distance is the number of positions at which the corresponding symbols are different.</p>
<b>Signature:</b>
```go
func HammingDistance(a, b string) (int, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/glNdQEA9HUi)</span></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
}
```
### <span id="Concat">Concat</span>
<p>Concatenates strings. <b>length</b> is the length of the concatenated string. If unsure, pass 0 or a negative number.</p>
<b>Signature:</b>
```go
func Concat(length int, str ...string) string
```
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
```go
import (
"fmt"
"github.com/duke-git/lancet/v2/strutil"
)
func main() {
result1 := strutil.Concat(12, "Hello", " ", "World", "!")
result2 := strutil.Concat(11, "Go", " ", "Language")
result3 := strutil.Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}
```

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

@@ -22,6 +22,7 @@ import (
"runtime"
"sort"
"strings"
"sync"
"github.com/duke-git/lancet/v2/validator"
)
@@ -866,3 +867,98 @@ func isCsvSupportedType(v interface{}) bool {
return false
}
}
// ChunkRead reads a block from the file at the specified offset and returns all lines within the block
// Play: https://go.dev/play/p/r0hPmKWhsgf
func ChunkRead(file *os.File, offset int64, size int, bufPool *sync.Pool) ([]string, error) {
buf := bufPool.Get().([]byte)[:size] // 从Pool获取缓冲区并调整大小
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: https://go.dev/play/p/teMXnCsdSEw
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

@@ -5,6 +5,8 @@ import (
"io"
"log"
"os"
"runtime"
"sync"
)
func ExampleIsExist() {
@@ -421,8 +423,69 @@ func ExampleReadFile() {
if err != nil {
return
}
fmt.Println(string(dat))
// Output:
// User-agent: *
// Disallow: /deny
}
func ExampleChunkRead() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100
filePath := "./testdata/test1.csv"
f, err := os.Open(filePath)
if err != nil {
return
}
defer f.Close()
var bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultChunkSizeMB*mb)
},
}
lines, err := ChunkRead(f, 0, 100, &bufPool)
if err != nil {
return
}
fmt.Println(lines[0])
fmt.Println(lines[1])
// Output:
// Lili,22,female
// Jim,21,male
}
func ExampleParallelChunkRead() {
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
filePath := "./testdata/test1.csv"
go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
var totalLines int
for lines := range linesCh {
totalLines += len(lines)
for _, line := range lines {
fmt.Println(line)
}
}
fmt.Println(totalLines)
// Output:
// Lili,22,female
// Jim,21,male
// 2
}

View File

@@ -4,7 +4,9 @@ import (
"io"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"testing"
"github.com/duke-git/lancet/v2/internal"
@@ -566,3 +568,54 @@ func TestCopyDir(t *testing.T) {
os.RemoveAll(dest)
}
func TestParallelChunkRead(t *testing.T) {
assert := internal.NewAssert(t, "TestParallelChunkRead")
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
numParsers := runtime.NumCPU()
linesCh := make(chan []string, numParsers)
filePath := "./testdata/test1.csv" // 替换为你的文件路径
go ParallelChunkRead(filePath, linesCh, defaultChunkSizeMB, numParsers)
var totalLines int
for lines := range linesCh {
totalLines += len(lines)
assert.Equal("Lili,22,female", lines[0])
assert.Equal("Jim,21,male", lines[1])
}
assert.Equal(2, totalLines)
}
func TestChunkRead(t *testing.T) {
assert := internal.NewAssert(t, "TestChunkRead")
const mb = 1024 * 1024
const defaultChunkSizeMB = 100 // 默认值
filePath := "./testdata/test1.csv" // 替换为你的文件路径
f, err := os.Open(filePath)
if err != nil {
return
}
defer f.Close()
var bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, 0, defaultChunkSizeMB*mb)
},
}
lines, err := ChunkRead(f, 0, 100, &bufPool)
assert.Equal("Lili,22,female", lines[0])
assert.Equal("Jim,21,male", lines[1])
}

View File

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

View File

@@ -136,6 +136,28 @@ 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.
// Play: https://go.dev/play/p/XlXHHtzCf7d
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,
)
}

View File

@@ -2,6 +2,7 @@ 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.
// Play: https://go.dev/play/p/dTBHJMQ0zD2
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")
@@ -18,6 +19,7 @@ func And[T any](predicates ...func(T) bool) func(T) bool {
// Nand returns a composed predicate that represents the logical NAND of a list of predicates.
// It evaluates to true only if all predicates evaluate to false for the given value.
// Play: https://go.dev/play/p/Rb-FdNGpgSO
func Nand[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
@@ -33,6 +35,7 @@ func Nand[T any](predicates ...func(T) bool) func(T) bool {
}
// Negate returns a predicate that represents the logical negation of this predicate.
// Play: https://go.dev/play/p/jbI8BtgFnVE
func Negate[T any](predicate func(T) bool) func(T) bool {
return func(value T) bool {
return !predicate(value)
@@ -41,6 +44,7 @@ func Negate[T any](predicate func(T) bool) func(T) bool {
// Or returns a composed predicate that represents the logical OR of a list of predicates.
// It evaluates to true if at least one of the predicates evaluates to true for the given value.
// Play: https://go.dev/play/p/LitCIsDFNDA
func Or[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
@@ -57,6 +61,7 @@ func Or[T any](predicates ...func(T) bool) func(T) bool {
// Nor returns a composed predicate that represents the logical NOR of a list of predicates.
// It evaluates to true only if all predicates evaluate to false for the given value.
// Play: https://go.dev/play/p/2KdCoBEOq84
func Nor[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")
@@ -73,6 +78,7 @@ func Nor[T any](predicates ...func(T) bool) func(T) bool {
// Xnor returns a composed predicate that represents the logical XNOR of a list of predicates.
// It evaluates to true only if all predicates evaluate to true or false for the given value.
// Play: https://go.dev/play/p/FJxko8SFbqc
func Xnor[T any](predicates ...func(T) bool) func(T) bool {
if len(predicates) < 2 {
panic("programming error: predicates count must be at least 2")

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

@@ -7,6 +7,10 @@ package maputil
import (
"fmt"
"reflect"
"sort"
"strings"
"golang.org/x/exp/constraints"
"github.com/duke-git/lancet/v2/slice"
)
@@ -375,8 +379,7 @@ func getFieldNameByJsonTag(structObj any, jsonTag string) string {
for i := 0; i < s.NumField(); i++ {
field := s.Field(i)
tag := field.Tag
name := tag.Get("json")
name, _, _ := strings.Cut(tag.Get("json"), ",")
if name == jsonTag {
return field.Name
}
@@ -384,3 +387,50 @@ func getFieldNameByJsonTag(structObj any, jsonTag string) string {
return ""
}
// ToSortedSlicesDefault converts a map to two slices sorted by key: one for the keys and another for the values.
func ToSortedSlicesDefault[K constraints.Ordered, V any](m map[K]V) ([]K, []V) {
keys := make([]K, 0, len(m))
// store the maps keys into a slice
for k := range m {
keys = append(keys, k)
}
// sort the slice of keys
sort.Slice(keys, func(i, j int) bool {
return keys[i] < keys[j]
})
// adjust the order of values according to the sorted keys
sortedValues := make([]V, len(keys))
for i, k := range keys {
sortedValues[i] = m[k]
}
return keys, sortedValues
}
// ToSortedSlicesWithComparator converts a map to two slices sorted by key and using a custom comparison function:
// one for the keys and another for the values.
func ToSortedSlicesWithComparator[K comparable, V any](m map[K]V, comparator func(a, b K) bool) ([]K, []V) {
keys := make([]K, 0, len(m))
// store the maps keys into a slice
for k := range m {
keys = append(keys, k)
}
// sort the key slice using the provided comparison function
sort.Slice(keys, func(i, j int) bool {
return comparator(keys[i], keys[j])
})
// adjust the order of values according to the sorted keys
sortedValues := make([]V, len(keys))
for i, k := range keys {
sortedValues[i] = m[k]
}
return keys, sortedValues
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"sort"
"strconv"
"time"
)
func ExampleKeys() {
@@ -450,3 +451,76 @@ func ExampleHasKey() {
// true
// false
}
func ExampleMapToStruct() {
personReqMap := map[string]any{
"name": "Nothin",
"max_age": 35,
"page": 1,
"pageSize": 10,
}
type PersonReq struct {
Name string `json:"name"`
MaxAge int `json:"max_age"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
var personReq PersonReq
_ = MapToStruct(personReqMap, &personReq)
fmt.Println(personReq)
// Output:
// {Nothin 35 1 10}
}
func ExampleToSortedSlicesDefault() {
m := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys, values := ToSortedSlicesDefault(m)
fmt.Println(keys)
fmt.Println(values)
// Output:
// [1 2 3]
// [a b c]
}
func ExampleToSortedSlicesWithComparator() {
m1 := map[time.Time]string{
time.Date(2024, 3, 31, 0, 0, 0, 0, time.UTC): "today",
time.Date(2024, 3, 30, 0, 0, 0, 0, time.UTC): "yesterday",
time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC): "tomorrow",
}
keys1, values1 := ToSortedSlicesWithComparator(m1, func(a, b time.Time) bool {
return a.Before(b)
})
m2 := map[int]string{
1: "a",
3: "c",
2: "b",
}
keys2, values2 := ToSortedSlicesWithComparator(m2, func(a, b int) bool {
return a > b
})
fmt.Println(keys1)
fmt.Println(values1)
fmt.Println(keys2)
fmt.Println(values2)
// Output:
// [2024-03-30 00:00:00 +0000 UTC 2024-03-31 00:00:00 +0000 UTC 2024-04-01 00:00:00 +0000 UTC]
// [yesterday today tomorrow]
// [3 2 1]
// [c b a]
}

View File

@@ -1,9 +1,11 @@
package maputil
import (
"math/cmplx"
"sort"
"strconv"
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
@@ -483,7 +485,7 @@ func TestMapToStruct(t *testing.T) {
Name string `json:"name"`
Age int `json:"age"`
Phone string `json:"phone"`
Addr *Address `json:"address"`
Addr *Address `json:"address,omitempty"`
}
Address struct {
@@ -511,3 +513,181 @@ func TestMapToStruct(t *testing.T) {
assert.Equal("test", p.Addr.Street)
assert.Equal(1, p.Addr.Number)
}
func TestToSortedSlicesDefault(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToSortedSlicesDefault")
testCases1 := []struct {
name string
inputMap map[string]any
expKeys []string
expValues []any
}{
{
name: "Empty Map",
inputMap: map[string]any{},
expKeys: []string{},
expValues: []any{},
},
{
name: "Unsorted Map",
inputMap: map[string]any{"c": 3, "a": 1.6, "b": 2},
expKeys: []string{"a", "b", "c"},
expValues: []any{1.6, 2, 3},
},
}
for _, tc := range testCases1 {
t.Run(tc.name, func(t *testing.T) {
keys, values := ToSortedSlicesDefault(tc.inputMap)
assert.Equal(tc.expKeys, keys)
assert.Equal(tc.expValues, values)
})
}
testCases2 := map[int64]string{
7: "seven",
3: "three",
5: "five",
}
actualK2, actualV2 := ToSortedSlicesDefault(testCases2)
case2Keys := []int64{3, 5, 7}
case2Values := []string{"three", "five", "seven"}
assert.Equal(case2Keys, actualK2)
assert.Equal(case2Values, actualV2)
}
func TestToSortedSlicesWithComparator(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestToSortedSlicesWithComparator")
type Account struct {
ID int
Name string
CreateTime time.Time
FavorComplexNumber complex128
Signal chan struct{}
}
type testCase struct {
name string
inputMap map[Account]any
lessFunc func(a, b Account) bool
expKeys []Account
expValues []any
}
now := time.Now()
tomorrow := now.AddDate(0, 0, 1)
yesterday := now.AddDate(0, 0, -1)
account1 := Account{ID: 1, Name: "cya", CreateTime: now, FavorComplexNumber: complex(1.2, 3),
Signal: make(chan struct{}, 10)}
account2 := Account{ID: 2, Name: "Cannian", CreateTime: tomorrow, FavorComplexNumber: complex(1.2, 2),
Signal: make(chan struct{}, 7)}
account3 := Account{ID: 3, Name: "Clauviou", CreateTime: yesterday, FavorComplexNumber: complex(3, 4),
Signal: make(chan struct{}, 3)}
account1.Signal <- struct{}{}
account2.Signal <- struct{}{}
account2.Signal <- struct{}{}
testCases := []testCase{
{
name: "Sorted by Account ID",
inputMap: map[Account]any{
account1: 100,
account2: 200,
account3: 300,
},
lessFunc: func(a, b Account) bool { return a.ID < b.ID },
expKeys: []Account{account1, account2, account3},
expValues: []any{100, 200, 300},
},
{
name: "Reverse Sorted by Account ID",
inputMap: map[Account]any{
account1: 100,
account2: 200,
account3: 300,
},
lessFunc: func(a, b Account) bool { return a.ID > b.ID },
expKeys: []Account{account3, account2, account1},
expValues: []any{300, 200, 100},
},
{
name: "Sorted by Account Name",
inputMap: map[Account]any{
account1: 100,
account2: 200,
account3: 300,
},
lessFunc: func(a, b Account) bool { return a.Name < b.Name },
expKeys: []Account{account2, account3, account1},
expValues: []any{200, 300, 100},
},
{
name: "Empty Map",
inputMap: map[Account]any{},
lessFunc: func(a, b Account) bool { return a.ID < b.ID },
expKeys: []Account{},
expValues: []any{},
},
{
name: "Sorted by Account CreateTime",
inputMap: map[Account]any{
account1: "now",
account2: "tomorrow",
account3: "yesterday",
},
lessFunc: func(a, b Account) bool { return a.CreateTime.Before(b.CreateTime) },
expKeys: []Account{account3, account1, account2},
expValues: []any{"yesterday", "now", "tomorrow"},
},
{
name: "Sorted by Account FavorComplexNumber",
inputMap: map[Account]any{
account1: "1.2+3i",
account2: "1.2+2i",
account3: "3+4i",
},
lessFunc: func(a, b Account) bool { return cmplx.Abs(a.FavorComplexNumber) < cmplx.Abs(b.FavorComplexNumber) },
expKeys: []Account{account2, account1, account3},
expValues: []any{"1.2+2i", "1.2+3i", "3+4i"},
},
{
name: "Sort by the buffer capacity of the channel",
inputMap: map[Account]any{
account1: 10,
account2: 7,
account3: 3,
},
lessFunc: func(a, b Account) bool {
return cap(a.Signal) < cap(b.Signal)
},
expKeys: []Account{account3, account2, account1},
expValues: []any{3, 7, 10},
},
{
name: "Sort by the number of elements in the channel",
inputMap: map[Account]any{
account1: 1,
account2: 2,
account3: 0,
},
lessFunc: func(a, b Account) bool { return len(a.Signal) < len(b.Signal) },
expKeys: []Account{account3, account1, account2},
expValues: []any{0, 1, 2},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
keys, values := ToSortedSlicesWithComparator(tc.inputMap, tc.lessFunc)
assert.Equal(tc.expKeys, keys)
assert.Equal(tc.expValues, values)
})
}
}

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: https://go.dev/play/p/vbCBrQHZEED
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
tmp := math.Pow(10.0, float64(n))
x *= T(tmp)
r := math.Floor(float64(x))
return r / tmp
}
// FloorToString round down to n decimal places.
// Play: https://go.dev/play/p/Qk9KPd2IdDb
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string {
tmp := math.Pow(10.0, float64(n))
x *= T(tmp)
r := math.Floor(float64(x))
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
return result
}
// CeilToFloat round up to n decimal places.
// Play: https://go.dev/play/p/8hOeSADZPCo
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
tmp := math.Pow(10.0, float64(n))
x *= T(tmp)
r := math.Ceil(float64(x))
return r / tmp
}
// CeilToString round up to n decimal places.
// Play: https://go.dev/play/p/wy5bYEyUKKG
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string {
tmp := math.Pow(10.0, float64(n))
x *= T(tmp)
r := math.Ceil(float64(x))
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
return result
}
@@ -253,7 +291,7 @@ func PointDistance(x1, y1, x2, y2 float64) float64 {
return math.Sqrt(c)
}
// IsPrimes checks if number is prime number.
// IsPrime checks if number is prime number.
// Play: https://go.dev/play/p/Rdd8UTHZJ7u
func IsPrime(n int) bool {
if n < 2 {
@@ -351,3 +389,9 @@ func Abs[T constraints.Integer | constraints.Float](x T) T {
return x
}
// Div returns the result of x divided by y.
// Play: https://go.dev/play/p/WLxDdGXXYat
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

@@ -28,7 +28,6 @@ import (
"strings"
"time"
"github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/slice"
)
@@ -109,6 +108,7 @@ type HttpClientConfig struct {
HandshakeTimeout time.Duration
ResponseTimeout time.Duration
Verbose bool
Proxy *url.URL
}
// defaultHttpClientConfig defalut client config.
@@ -164,6 +164,11 @@ func NewHttpClientWithConfig(config *HttpClientConfig) *HttpClient {
client.TLS = config.TLSConfig
}
if config.Proxy != nil {
transport := client.Client.Transport.(*http.Transport)
transport.Proxy = http.ProxyURL(config.Proxy)
}
return client
}
@@ -363,11 +368,20 @@ func validateRequest(req *HttpRequest) error {
// Play: https://go.dev/play/p/pFqMkM40w9z
func StructToUrlValues(targetStruct any) (url.Values, error) {
result := url.Values{}
s, err := convertor.StructToMap(targetStruct)
var m map[string]interface{}
jsonBytes, err := json.Marshal(targetStruct)
if err != nil {
return nil, err
}
for k, v := range s {
err = json.Unmarshal(jsonBytes, &m)
if err != nil {
return nil, err
}
for k, v := range m {
result.Add(k, fmt.Sprintf("%v", v))
}

View File

@@ -5,12 +5,12 @@ import (
"encoding/json"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
"time"
"github.com/duke-git/lancet/v2/internal"
)
@@ -23,7 +23,8 @@ func TestHttpGet(t *testing.T) {
resp, err := HttpGet(url, header)
if err != nil {
log.Fatal(err)
t.Log("net error: " + err.Error())
return
}
body, _ := io.ReadAll(resp.Body)
@@ -44,8 +45,10 @@ func TestHttpPost(t *testing.T) {
resp, err := HttpPost(url, header, nil, bodyParams)
if err != nil {
log.Fatal(err)
t.Log("net error: " + err.Error())
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
}
@@ -54,21 +57,18 @@ func TestHttpPostFormData(t *testing.T) {
apiUrl := "https://jsonplaceholder.typicode.com/todos"
header := map[string]string{
"Content-Type": "application/x-www-form-urlencoded",
// "Content-Type": "multipart/form-data",
}
postData := url.Values{}
postData.Add("userId", "1")
postData.Add("title", "TestToDo")
// postData := make(map[string]string)
// postData["userId"] = "1"
// postData["title"] = "title"
resp, err := HttpPost(apiUrl, header, nil, postData)
if err != nil {
log.Fatal(err)
t.Log("net error: " + err.Error())
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
}
@@ -88,8 +88,10 @@ func TestHttpPut(t *testing.T) {
resp, err := HttpPut(url, header, nil, bodyParams)
if err != nil {
log.Fatal(err)
t.Log("net error: ", err.Error())
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
}
@@ -109,8 +111,10 @@ func TestHttpPatch(t *testing.T) {
resp, err := HttpPatch(url, header, nil, bodyParams)
if err != nil {
log.Fatal(err)
t.Log("net error: ", err.Error())
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
}
@@ -119,8 +123,10 @@ func TestHttpDelete(t *testing.T) {
url := "https://jsonplaceholder.typicode.com/todos/1"
resp, err := HttpDelete(url)
if err != nil {
log.Fatal(err)
t.Log("net error: ", err.Error())
return
}
body, _ := io.ReadAll(resp.Body)
t.Log("response: ", resp.StatusCode, string(body))
}
@@ -147,7 +153,8 @@ func TestParseResponse(t *testing.T) {
resp, err := HttpGet(url, header)
if err != nil {
log.Fatal(err)
t.Log("net error: ", err.Error())
return
}
type Todo struct {
@@ -160,8 +167,10 @@ func TestParseResponse(t *testing.T) {
toDoResp := &Todo{}
err = ParseHttpResponse(resp, toDoResp)
if err != nil {
log.Fatal(err)
t.Log("net error: ", err.Error())
return
}
t.Log("response: ", toDoResp)
}
@@ -178,7 +187,8 @@ func TestHttpClient_Get(t *testing.T) {
httpClient := NewHttpClient()
resp, err := httpClient.SendRequest(request)
if err != nil || resp.StatusCode != 200 {
log.Fatal(err)
t.Log("net error: ", err.Error())
return
}
type Todo struct {
@@ -215,7 +225,8 @@ func TestHttpClent_Post(t *testing.T) {
httpClient := NewHttpClient()
resp, err := httpClient.SendRequest(request)
if err != nil {
log.Fatal(err)
t.Log("net error: ", err.Error())
return
}
body, _ := io.ReadAll(resp.Body)
@@ -227,16 +238,25 @@ func TestStructToUrlValues(t *testing.T) {
assert := internal.NewAssert(t, "TestStructToUrlValues")
type CommReq struct {
Version string `json:"version"`
}
type TodoQuery struct {
Id int `json:"id"`
UserId int `json:"userId"`
Name string `json:"name,omitempty"`
Id int `json:"id"`
UserId int `json:"userId"`
Name string `json:"name,omitempty"`
CommReq `json:",inline"`
}
item1 := TodoQuery{
Id: 1,
UserId: 123,
Name: "",
CommReq: CommReq{
Version: "1.0",
},
}
todoValues, err := StructToUrlValues(item1)
if err != nil {
t.Errorf("params is invalid: %v", err)
@@ -245,19 +265,10 @@ func TestStructToUrlValues(t *testing.T) {
assert.Equal("1", todoValues.Get("id"))
assert.Equal("123", todoValues.Get("userId"))
assert.Equal("", todoValues.Get("name"))
item2 := TodoQuery{
Id: 2,
UserId: 456,
}
queryValues2, _ := StructToUrlValues(item2)
assert.Equal("2", queryValues2.Get("id"))
assert.Equal("456", queryValues2.Get("userId"))
assert.Equal("", queryValues2.Get("name"))
assert.Equal("1.0", todoValues.Get("version"))
}
func handleFileRequest(t *testing.T, w http.ResponseWriter, r *http.Request) {
func handleFileRequest(t *testing.T, _ http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(1024)
if err != nil {
t.Fatal(err)
@@ -361,3 +372,25 @@ func TestSendRequestWithFilePath(t *testing.T) {
t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode)
}
}
func TestProxy(t *testing.T) {
config := &HttpClientConfig{
HandshakeTimeout: 20 * time.Second,
ResponseTimeout: 40 * time.Second,
// Use the proxy ip to add it here
//Proxy: &url.URL{
// Scheme: "http",
// Host: "46.17.63.166:18888",
//},
}
httpClient := NewHttpClientWithConfig(config)
resp, err := httpClient.Get("https://www.ipplus360.com/getLocation")
if err != nil {
t.Log("net error: ", err.Error())
return
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected %d, got %d", http.StatusOK, resp.StatusCode)
}
}

View File

@@ -17,7 +17,7 @@ import (
)
const (
MaximumCapacity = 1 << 31
MaximumCapacity = math.MaxInt>>1 + 1
Numeral = "0123456789"
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -157,7 +157,7 @@ func random(s string, length int) string {
cache, remain = rn.Int63(), letterIdMax
}
// 从可用字符的字符串中随机选择一个字符
if idx := int(cache & letterIdMask); idx < len(s) {
if idx := int(cache & letterIdMask); idx < strLength {
bytes[i] = s[idx]
i--
}

View File

@@ -45,7 +45,7 @@ func RetryTimes(n uint) Option {
}
// RetryWithCustomBackoff set abitary custom backoff strategy
// todo: Add playground link
// Play: todo
func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
if backoffStrategy == nil {
panic("programming error: backoffStrategy must be not nil")
@@ -57,7 +57,7 @@ func RetryWithCustomBackoff(backoffStrategy BackoffStrategy) Option {
}
// RetryWithLinearBackoff set linear strategy backoff
// todo: Add playground link
// Play: todo
func RetryWithLinearBackoff(interval time.Duration) Option {
if interval <= 0 {
panic("programming error: retry interval should not be lower or equal to 0")
@@ -71,7 +71,7 @@ func RetryWithLinearBackoff(interval time.Duration) Option {
}
// RetryWithExponentialWithJitterBackoff set exponential strategy backoff
// todo: Add playground link
// Play: todo
func RetryWithExponentialWithJitterBackoff(interval time.Duration, base uint64, maxJitter time.Duration) Option {
if interval <= 0 {
panic("programming error: retry interval should not be lower or equal to 0")

View File

@@ -50,12 +50,23 @@ func ContainBy[T any](slice []T, predicate func(item T) bool) bool {
// ContainSubSlice check if the slice contain a given subslice or not.
// Play: https://go.dev/play/p/bcuQ3UT6Sev
func ContainSubSlice[T comparable](slice, subSlice []T) bool {
for _, v := range subSlice {
if !Contain(slice, v) {
if len(subSlice) == 0 {
return true
}
if len(slice) == 0 {
return false
}
elementMap := make(map[T]struct{}, len(slice))
for _, item := range slice {
elementMap[item] = struct{}{}
}
for _, item := range subSlice {
if _, ok := elementMap[item]; !ok {
return false
}
}
return true
}
@@ -81,35 +92,41 @@ func Chunk[T any](slice []T, size int) [][]T {
return result
}
// Compact creates an slice with all falsey values removed. The values false, nil, 0, and "" are falsey.
// Compact creates a slice with all falsey values removed. The values false, nil, 0, and "" are falsey.
// Play: https://go.dev/play/p/pO5AnxEr3TK
func Compact[T comparable](slice []T) []T {
var zero T
result := []T{}
result := make([]T, 0, len(slice))
for _, v := range slice {
if v != zero {
result = append(result, v)
}
}
return result
return result[:len(result):len(result)]
}
// Concat creates a new slice concatenating slice with any additional slices.
// Play: https://go.dev/play/p/gPt-q7zr5mk
func Concat[T any](slice []T, slices ...[]T) []T {
result := append([]T{}, slice...)
totalLen := len(slice)
for _, v := range slices {
result = append(result, v...)
totalLen += len(v)
}
result := make([]T, 0, totalLen)
result = append(result, slice...)
for _, s := range slices {
result = append(result, s...)
}
return result
}
// Difference creates an slice of whose element in slice but not in comparedSlice.
// Difference creates a slice of whose element in slice but not in comparedSlice.
// Play: https://go.dev/play/p/VXvadzLzhDa
func Difference[T comparable](slice, comparedSlice []T) []T {
result := []T{}
@@ -755,21 +772,14 @@ func UpdateAt[T any](slice []T, index int, value T) []T {
// Play: https://go.dev/play/p/AXw0R3ZTE6a
func Unique[T comparable](slice []T) []T {
result := []T{}
for i := 0; i < len(slice); i++ {
v := slice[i]
skip := true
for j := range result {
if v == result[j] {
skip = false
break
}
}
if skip {
result = append(result, v)
exists := map[T]bool{}
for _, t := range slice {
if exists[t] {
continue
}
exists[t] = true
result = append(result, t)
}
return result
}
@@ -826,7 +836,11 @@ func UnionBy[T any, V comparable](predicate func(item T) V, slices ...[]T) []T {
// Merge all given slices into one slice.
// Play: https://go.dev/play/p/lbjFp784r9N
func Merge[T any](slices ...[]T) []T {
result := make([]T, 0)
totalLen := 0
for _, v := range slices {
totalLen += len(v)
}
result := make([]T, 0, totalLen)
for _, v := range slices {
result = append(result, v...)
@@ -1170,6 +1184,22 @@ func AppendIfAbsent[T comparable](slice []T, item T) []T {
return slice
}
// SetToDefaultIf sets elements to their default value if they match the given predicate.
// It retains the positions of the elements in the slice.
// It returns slice of T and the count of modified slice items
// Play: https://go.dev/play/p/9AXGlPRC0-A
func SetToDefaultIf[T any](slice []T, predicate func(T) bool) ([]T, int) {
var count int
for i := 0; i < len(slice); i++ {
if predicate(slice[i]) {
var zeroValue T
slice[i] = zeroValue
count++
}
}
return slice, count
}
// KeyBy converts a slice to a map based on a callback function.
// Play: https://go.dev/play/p/uXod2LWD1Kg
func KeyBy[T any, U comparable](slice []T, iteratee func(item T) U) map[U]T {
@@ -1223,6 +1253,30 @@ func Partition[T any](slice []T, predicates ...func(item T) bool) [][]T {
return result
}
// Breaks a list into two parts at the point where the predicate for the first time is true.
// Play: Todo
func Break[T any](values []T, predicate func(T) bool) ([]T, []T) {
a := make([]T, 0)
b := make([]T, 0)
if len(values) == 0 {
return a, b
}
matched := false
for _, value := range values {
if !matched && predicate(value) {
matched = true
}
if matched {
b = append(b, value)
} else {
a = append(a, value)
}
}
return a, b
}
// Random get a random item of slice, return idx=-1 when slice is empty
// Play: https://go.dev/play/p/UzpGQptWppw
func Random[T any](slice []T) (val T, idx int) {
@@ -1233,3 +1287,35 @@ func Random[T any](slice []T) (val T, idx int) {
idx = random.RandInt(0, len(slice))
return slice[idx], idx
}
// RightPadding adds padding to the right end of a slice.
// Play: Todo
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
if paddingLength == 0 {
return slice
}
for i := 0; i < paddingLength; i++ {
slice = append(slice, paddingValue)
}
return slice
}
// LeftPadding adds padding to the left begin of a slice.
// Play: Todo
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
if paddingLength == 0 {
return slice
}
paddedSlice := make([]T, len(slice)+paddingLength)
i := 0
for ; i < paddingLength; i++ {
paddedSlice[i] = paddingValue
}
for j := 0; j < len(slice); j++ {
paddedSlice[i] = slice[j]
i++
}
return paddedSlice
}

View File

@@ -1102,3 +1102,42 @@ func ExampleRandom() {
// Output:
// okk
}
func ExampleSetToDefaultIf() {
strs := []string{"a", "b", "a", "c", "d", "a"}
modifiedStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
fmt.Println(modifiedStrs)
fmt.Println(count)
// Output:
// [ b c d ]
// 3
}
func ExampleBreak() {
nums := []int{1, 2, 3, 4, 5}
even := func(n int) bool { return n%2 == 0 }
resultEven, resultAfterFirstEven := Break(nums, even)
fmt.Println(resultEven)
fmt.Println(resultAfterFirstEven)
// Output:
// [1]
// [2 3 4 5]
}
func ExampleRightPadding() {
nums := []int{1, 2, 3, 4, 5}
padded := RightPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [1 2 3 4 5 0 0 0]
}
func ExampleLeftPadding() {
nums := []int{1, 2, 3, 4, 5}
padded := LeftPadding(nums, 0, 3)
fmt.Println(padded)
// Output:
// [0 0 0 1 2 3 4 5]
}

View File

@@ -2,11 +2,11 @@ package slice
import (
"fmt"
"github.com/duke-git/lancet/v2/internal"
"math"
"reflect"
"strconv"
"testing"
"github.com/duke-git/lancet/v2/internal"
)
func TestContain(t *testing.T) {
@@ -108,6 +108,19 @@ func TestConcat(t *testing.T) {
assert.Equal([]int{1, 2, 3, 4, 5}, Concat([]int{1, 2, 3}, []int{4}, []int{5}))
}
func BenchmarkConcat(b *testing.B) {
slice1 := []int{1, 2, 3}
slice2 := []int{4, 5, 6}
slice3 := []int{7, 8, 9}
for i := 0; i < b.N; i++ {
result := Concat(slice1, slice2, slice3)
if len(result) == 0 {
b.Fatal("unexpected empty result")
}
}
}
func TestEqual(t *testing.T) {
t.Parallel()
@@ -1218,3 +1231,192 @@ func TestRandom(t *testing.T) {
assert.Greater(len(arr), idx)
assert.Equal(arr[idx], val)
}
func TestSetToDefaultIf(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "SetToDefaultIf")
// Subtest for strings
t.Run("strings", func(t *testing.T) {
strs := []string{"a", "b", "a", "c", "d", "a"}
actualStrs, count := SetToDefaultIf(strs, func(s string) bool { return "a" == s })
assert.Equal([]string{"", "b", "", "c", "d", ""}, actualStrs)
assert.Equal(3, count)
})
// Subtest for integers
t.Run("integers", func(t *testing.T) {
ints := []int{1, 2, 3, 2, 4, 2}
actualInts, count := SetToDefaultIf(ints, func(i int) bool { return i == 2 })
assert.Equal([]int{1, 0, 3, 0, 4, 0}, actualInts)
assert.Equal(3, count)
})
// Subtest for floating-point numbers
t.Run("floats", func(t *testing.T) {
floats := []float64{1.1, 2.2, 3.3, 2.2, 4.4, 2.2}
actualFloats, count := SetToDefaultIf(floats, func(f float64) bool { return f == 2.2 })
assert.Equal([]float64{1.1, 0, 3.3, 0, 4.4, 0}, actualFloats)
assert.Equal(3, count)
})
// Subtest for booleans
t.Run("booleans", func(t *testing.T) {
bools := []bool{true, false, true, true, false}
actualBools, count := SetToDefaultIf(bools, func(b bool) bool { return b })
assert.Equal([]bool{false, false, false, false, false}, actualBools)
assert.Equal(3, count)
})
// Subtest for a custom type
type customType struct {
field string
}
t.Run("customType", func(t *testing.T) {
customs := []customType{{"a"}, {"b"}, {"a"}, {"c"}}
actualCustoms, count := SetToDefaultIf(customs, func(c customType) bool { return c.field == "a" })
expected := []customType{{""}, {"b"}, {""}, {"c"}}
assert.Equal(expected, actualCustoms)
assert.Equal(2, count)
})
// Subtest for slice of integers
t.Run("sliceOfInts", func(t *testing.T) {
sliceOfInts := [][]int{{1, 2}, {3, 4}, {5, 6}, {1, 2}}
actualSlice, count := SetToDefaultIf(sliceOfInts, func(s []int) bool { return reflect.DeepEqual(s, []int{1, 2}) })
expected := [][]int{nil, {3, 4}, {5, 6}, nil}
assert.Equal(expected, actualSlice)
assert.Equal(2, count)
})
// Subtest for maps (simple use case)
t.Run("mapOfStringToInts", func(t *testing.T) {
maps := []map[string]int{{"a": 1}, {"b": 2}, {"a": 1}, {"c": 3}}
actualMaps, count := SetToDefaultIf(maps, func(m map[string]int) bool { _, ok := m["a"]; return ok })
expected := []map[string]int{nil, {"b": 2}, nil, {"c": 3}}
assert.Equal(expected, actualMaps)
assert.Equal(2, count)
})
// Subtest for pointers to integers
t.Run("pointersToInts", func(t *testing.T) {
one, two, three := 1, 2, 3
pointers := []*int{&one, &two, &one, &three}
actualPointers, count := SetToDefaultIf(pointers, func(p *int) bool { return p != nil && *p == 1 })
expected := []*int{nil, &two, nil, &three}
assert.Equal(expected, actualPointers)
assert.Equal(2, count)
})
// Subtest for channels
t.Run("channels", func(t *testing.T) {
ch1, ch2 := make(chan int), make(chan int)
channels := []chan int{ch1, ch2, ch1}
actualChannels, count := SetToDefaultIf(channels, func(ch chan int) bool { return ch == ch1 })
expected := []chan int{nil, ch2, nil}
assert.Equal(expected, actualChannels)
assert.Equal(2, count)
})
// Subtest for interfaces
t.Run("interfaces", func(t *testing.T) {
var i1, i2 interface{} = "hello", 42
interfaces := []interface{}{i1, i2, i1}
actualInterfaces, count := SetToDefaultIf(interfaces, func(i interface{}) bool { _, ok := i.(string); return ok })
expected := []interface{}{nil, 42, nil}
assert.Equal(expected, actualInterfaces)
assert.Equal(2, count)
})
// Subtest for complex structs
t.Run("complexStructs", func(t *testing.T) {
type ComplexStruct struct {
Name string
Value int
Data []byte
}
cs1, cs2 := ComplexStruct{Name: "Test", Value: 1, Data: []byte{1, 2, 3}}, ComplexStruct{Name: "Another", Value: 2, Data: []byte{4, 5, 6}}
complexStructs := []ComplexStruct{cs1, cs2, cs1}
actualComplexStructs, count := SetToDefaultIf(complexStructs, func(cs ComplexStruct) bool { return cs.Name == "Test" })
expected := []ComplexStruct{{}, cs2, {}}
assert.Equal(expected, actualComplexStructs)
assert.Equal(2, count)
})
// Subtest for uints
t.Run("uints", func(t *testing.T) {
uints := []uint{1, 2, 3, 2, 4, 2}
actualUints, count := SetToDefaultIf(uints, func(u uint) bool { return u == 2 })
assert.Equal([]uint{1, 0, 3, 0, 4, 0}, actualUints)
assert.Equal(3, count)
})
// Subtest for float32
t.Run("float32s", func(t *testing.T) {
floats := []float32{1.1, 2.2, 3.3, 2.2, 4.4, 2.2}
actualFloats, count := SetToDefaultIf(floats, func(f float32) bool { return f == 2.2 })
assert.Equal([]float32{1.1, 0, 3.3, 0, 4.4, 0}, actualFloats)
assert.Equal(3, count)
})
// Subtest for []byte
t.Run("byteSlices", func(t *testing.T) {
bytes := [][]byte{{'a', 'b'}, {'c', 'd'}, {'a', 'b'}}
actualBytes, count := SetToDefaultIf(bytes, func(b []byte) bool { return string(b) == "ab" })
expected := [][]byte{nil, {'c', 'd'}, nil}
assert.Equal(expected, actualBytes)
assert.Equal(2, count)
})
}
func TestBreak(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestBreak")
// Test with integers
nums := []int{1, 2, 3, 4, 5}
even := func(n int) bool { return n%2 == 0 }
resultEven, resultAfterFirstEven := Break(nums, even)
assert.Equal([]int{1}, resultEven)
assert.Equal([]int{2, 3, 4, 5}, resultAfterFirstEven)
// Test with strings
strings := []string{"apple", "banana", "cherry", "date", "elderberry"}
startsWithA := func(s string) bool { return s[0] == 'a' }
resultStartsWithA, resultAfterFirstStartsWithA := Break(strings, startsWithA)
assert.Equal([]string{}, resultStartsWithA)
assert.Equal([]string{"apple", "banana", "cherry", "date", "elderberry"}, resultAfterFirstStartsWithA)
// Test with empty slice
emptySlice := []int{}
resultEmpty, _ := Break(emptySlice, even)
assert.Equal([]int{}, resultEmpty)
// Test with all elements satisfying the predicate
allEven := []int{2, 4, 6, 8, 10}
emptyResult, resultAllEven := Break(allEven, even)
assert.Equal([]int{2, 4, 6, 8, 10}, resultAllEven)
assert.Equal([]int{}, emptyResult)
// Test with no elements satisfying the predicate
allOdd := []int{1, 3, 5, 7, 9}
resultAllOdd, emptyResult := Break(allOdd, even)
assert.Equal([]int{1, 3, 5, 7, 9}, resultAllOdd)
assert.Equal([]int{}, emptyResult)
}
func TestRightPaddingAndLeftPadding(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "RightPaddingAndLeftPadding")
// Test with integers
nums := []int{1, 2, 3, 4, 5}
padded := LeftPadding(RightPadding(nums, 0, 3), 0, 3)
assert.Equal([]int{0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0}, padded)
}

View File

@@ -4,6 +4,7 @@
package strutil
import (
"errors"
"regexp"
"strings"
"unicode"
@@ -584,7 +585,7 @@ func RemoveWhiteSpace(str string, repalceAll bool) string {
}
// SubInBetween return substring between the start and end position(excluded) of source string.
// Play: todo
// Play: https://go.dev/play/p/EDbaRvjeNsv
func SubInBetween(str string, start string, end string) string {
if _, after, ok := strings.Cut(str, start); ok {
if before, _, ok := strings.Cut(after, end); ok {
@@ -594,3 +595,46 @@ func SubInBetween(str string, start string, end string) string {
return ""
}
// HammingDistance calculates the Hamming distance between two strings.
// The Hamming distance is the number of positions at which the corresponding symbols are different.
// This func returns an error if the input strings are of unequal lengths.
// Play: https://go.dev/play/p/glNdQEA9HUi
func HammingDistance(a, b string) (int, error) {
if len(a) != len(b) {
return -1, errors.New("a length and b length are unequal")
}
ar := []rune(a)
br := []rune(b)
var distance int
for i, codepoint := range ar {
if codepoint != br[i] {
distance++
}
}
return distance, nil
}
// Concat uses the strings.Builder to concatenate the input strings.
// - `length` is the expected length of the concatenated string.
// - if you are unsure about the length of the string to be concatenated, please pass 0 or a negative number.
func Concat(length int, str ...string) string {
if len(str) == 0 {
return ""
}
sb := strings.Builder{}
if length <= 0 {
sb.Grow(len(str[0]) * len(str))
} else {
sb.Grow(length)
}
for _, s := range str {
sb.WriteString(s)
}
return sb.String()
}

View File

@@ -667,3 +667,30 @@ func ExampleSubInBetween() {
// abc
// bc
}
func ExampleHammingDistance() {
result, _ := HammingDistance("abc", "def")
fmt.Println(result)
result, _ = HammingDistance("name", "namf")
fmt.Println(result)
// Output:
// 3
// 1
}
func ExampleConcat() {
result1 := Concat(12, "Hello", " ", "World", "!")
result2 := Concat(11, "Go", " ", "Language")
result3 := Concat(0, "An apple a ", "day", "keeps the", " doctor away")
fmt.Println(result1)
fmt.Println(result2)
fmt.Println(result3)
// Output:
// Hello World!
// Go Language
// An apple a daykeeps the doctor away
}

View File

@@ -561,6 +561,7 @@ func TestContainsAny(t *testing.T) {
}
func TestRemoveWhiteSpace(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestRemoveWhiteSpace")
str := " hello \r\n \t world"
@@ -571,6 +572,7 @@ func TestRemoveWhiteSpace(t *testing.T) {
}
func TestSubInBetween(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestSubInBetween")
str := "abcde"
@@ -581,3 +583,40 @@ func TestSubInBetween(t *testing.T) {
assert.Equal("", SubInBetween(str, "a", ""))
assert.Equal("", SubInBetween(str, "a", "f"))
}
func TestHammingDistance(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "HammingDistance")
hd := func(a, b string) int {
c, _ := HammingDistance(a, b)
return c
}
assert.Equal(0, hd(" ", " "))
assert.Equal(1, hd(" ", "c"))
assert.Equal(1, hd("a", "d"))
assert.Equal(1, hd("a", " "))
assert.Equal(1, hd("a", "f"))
assert.Equal(0, hd("", ""))
assert.Equal(-1, hd("abc", "ab"))
assert.Equal(3, hd("abc", "def"))
assert.Equal(-1, hd("kitten", "sitting"))
assert.Equal(1, hd("ö", "ü"))
assert.Equal(0, hd("日本語", "日本語"))
assert.Equal(3, hd("日本語", "語日本"))
}
func TestConcat(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestConcat")
assert.Equal("", Concat(0))
assert.Equal("a", Concat(1, "a"))
assert.Equal("ab", Concat(2, "a", "b"))
assert.Equal("abc", Concat(3, "a", "b", "c"))
assert.Equal("abc", Concat(3, "a", "", "b", "c", ""))
assert.Equal("你好,世界!", Concat(0, "你好", "", "", "世界!", ""))
assert.Equal("Hello World!", Concat(0, "Hello", " Wo", "r", "ld!", ""))
}

View File

@@ -8,6 +8,7 @@ import (
"encoding/json"
"fmt"
"net"
"net/mail"
"net/url"
"reflect"
"regexp"
@@ -24,7 +25,7 @@ var (
intStrMatcher *regexp.Regexp = regexp.MustCompile(`^[\+-]?\d+$`)
urlMatcher *regexp.Regexp = regexp.MustCompile(`^((ftp|http|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`)
dnsMatcher *regexp.Regexp = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`)
emailMatcher *regexp.Regexp = regexp.MustCompile(`\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`)
emailMatcher *regexp.Regexp = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
chineseMobileMatcher *regexp.Regexp = regexp.MustCompile(`^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$`)
chineseIdMatcher *regexp.Regexp = regexp.MustCompile(`^(\d{17})([0-9]|X|x)$`)
chineseMatcher *regexp.Regexp = regexp.MustCompile("[\u4e00-\u9fa5]")
@@ -264,7 +265,10 @@ func IsDns(dns string) bool {
// IsEmail check if the string is a email address.
// Play: https://go.dev/play/p/Os9VaFlT33G
func IsEmail(email string) bool {
return emailMatcher.MatchString(email)
_, err := mail.ParseAddress(email)
return err == nil
// return emailMatcher.MatchString(email)
}
// IsChineseMobile check if the string is chinese mobile number.

View File

@@ -285,6 +285,7 @@ func TestIsEmail(t *testing.T) {
assert := internal.NewAssert(t, "TestIsEmail")
assert.Equal(true, IsEmail("abc@xyz.com"))
assert.Equal(false, IsEmail("@abc@xyz.com"))
assert.Equal(false, IsEmail("a.b@@com"))
}